2017-01-06 00:14:12 +00:00
|
|
|
"use strict";
|
2017-01-09 21:28:20 +00:00
|
|
|
|
2020-07-08 01:56:44 +00:00
|
|
|
import { WorkerResult, WorkerFileUpdate, WorkerBuildStep, WorkerMessage, WorkerError, Dependency, SourceLine, CodeListing, CodeListingMap, Segment, WorkerOutput } from "../common/workertypes";
|
2018-10-05 13:47:15 +00:00
|
|
|
|
|
|
|
declare var WebAssembly;
|
|
|
|
declare function importScripts(path:string);
|
|
|
|
declare function postMessage(msg);
|
|
|
|
|
2019-09-29 18:41:29 +00:00
|
|
|
const emglobal : any = this['window'] || this['global'] || this;
|
|
|
|
const ENVIRONMENT_IS_WEB = typeof window === 'object';
|
|
|
|
const ENVIRONMENT_IS_WORKER = typeof importScripts === 'function';
|
|
|
|
|
2018-07-10 04:44:17 +00:00
|
|
|
// WebAssembly module cache
|
|
|
|
// TODO: leaks memory even when disabled...
|
|
|
|
var _WASM_module_cache = {};
|
2020-07-13 22:15:48 +00:00
|
|
|
var CACHE_WASM_MODULES = true; // if false, use asm.js only
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function getWASMModule(module_id:string) {
|
2018-07-10 04:44:17 +00:00
|
|
|
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
|
2018-10-05 13:47:15 +00:00
|
|
|
function moduleInstFn(module_id:string) {
|
2018-07-10 04:44:17 +00:00
|
|
|
return function(imports,ri) {
|
|
|
|
var mod = getWASMModule(module_id);
|
|
|
|
var inst = new WebAssembly.Instance(mod, imports);
|
|
|
|
ri(inst);
|
|
|
|
return inst.exports;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-25 21:04:15 +00:00
|
|
|
// get platform ID without . emulator
|
|
|
|
function getBasePlatform(platform : string) : string {
|
|
|
|
return platform.split('.')[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
// get platform ID without - specialization
|
|
|
|
function getRootPlatform(platform : string) : string {
|
|
|
|
return platform.split('-')[0];
|
|
|
|
}
|
|
|
|
|
2019-04-03 21:00:05 +00:00
|
|
|
function getRootBasePlatform(platform : string) : string {
|
|
|
|
return getRootPlatform(getBasePlatform(platform));
|
|
|
|
}
|
|
|
|
|
2017-01-26 05:09:57 +00:00
|
|
|
var PLATFORM_PARAMS = {
|
2018-12-05 19:05:10 +00:00
|
|
|
'vcs': {
|
|
|
|
code_start: 0x1000,
|
|
|
|
code_size: 0xf000,
|
|
|
|
data_start: 0x80,
|
|
|
|
data_size: 0x80,
|
|
|
|
},
|
2017-01-26 05:09:57 +00:00
|
|
|
'mw8080bw': {
|
|
|
|
code_start: 0x0,
|
2017-05-02 13:09:53 +00:00
|
|
|
rom_size: 0x2000,
|
2017-01-26 05:09:57 +00:00
|
|
|
data_start: 0x2000,
|
|
|
|
data_size: 0x400,
|
2017-04-22 01:56:49 +00:00
|
|
|
stack_end: 0x2400,
|
2017-01-26 05:09:57 +00:00
|
|
|
},
|
|
|
|
'vicdual': {
|
|
|
|
code_start: 0x0,
|
2017-05-02 13:09:53 +00:00
|
|
|
rom_size: 0x4020,
|
2017-02-18 22:50:51 +00:00
|
|
|
data_start: 0xe400,
|
2017-01-26 05:09:57 +00:00
|
|
|
data_size: 0x400,
|
2017-04-22 01:56:49 +00:00
|
|
|
stack_end: 0xe800,
|
2017-01-26 05:09:57 +00:00
|
|
|
},
|
|
|
|
'galaxian': {
|
|
|
|
code_start: 0x0,
|
2017-05-02 13:09:53 +00:00
|
|
|
rom_size: 0x4000,
|
2017-01-26 05:09:57 +00:00
|
|
|
data_start: 0x4000,
|
|
|
|
data_size: 0x400,
|
2017-04-22 01:56:49 +00:00
|
|
|
stack_end: 0x4800,
|
2017-01-26 05:09:57 +00:00
|
|
|
},
|
2017-02-15 21:03:52 +00:00
|
|
|
'galaxian-scramble': {
|
|
|
|
code_start: 0x0,
|
2017-05-02 13:09:53 +00:00
|
|
|
rom_size: 0x5020,
|
2017-02-15 21:03:52 +00:00
|
|
|
data_start: 0x4000,
|
|
|
|
data_size: 0x400,
|
2017-04-22 01:56:49 +00:00
|
|
|
stack_end: 0x4800,
|
2017-02-15 21:03:52 +00:00
|
|
|
},
|
2019-08-06 19:36:28 +00:00
|
|
|
'williams': {
|
|
|
|
code_start: 0x0,
|
|
|
|
rom_size: 0xc000,
|
|
|
|
data_start: 0x9800,
|
|
|
|
data_size: 0x2800,
|
|
|
|
stack_end: 0xc000,
|
2020-06-09 22:32:29 +00:00
|
|
|
//extra_compile_args: ['--vectrex'],
|
2020-06-08 21:36:33 +00:00
|
|
|
extra_link_files: ['williams.scr', 'libcmoc-crt-vec.a', 'libcmoc-std-vec.a'],
|
|
|
|
extra_link_args: ['-swilliams.scr', '-lcmoc-crt-vec', '-lcmoc-std-vec'],
|
2019-08-06 19:36:28 +00:00
|
|
|
},
|
2017-01-29 21:06:05 +00:00
|
|
|
'williams-z80': {
|
|
|
|
code_start: 0x0,
|
2017-05-02 13:09:53 +00:00
|
|
|
rom_size: 0x9800,
|
2017-03-28 16:22:33 +00:00
|
|
|
data_start: 0x9800,
|
|
|
|
data_size: 0x2800,
|
2017-04-22 01:56:49 +00:00
|
|
|
stack_end: 0xc000,
|
2017-01-29 21:06:05 +00:00
|
|
|
},
|
2017-02-05 04:19:54 +00:00
|
|
|
'vector-z80color': {
|
2017-02-01 18:21:17 +00:00
|
|
|
code_start: 0x0,
|
2017-05-02 13:09:53 +00:00
|
|
|
rom_size: 0x8000,
|
2017-02-05 04:19:54 +00:00
|
|
|
data_start: 0xe000,
|
|
|
|
data_size: 0x2000,
|
2017-04-22 01:56:49 +00:00
|
|
|
stack_end: 0x0,
|
2017-02-01 18:21:17 +00:00
|
|
|
},
|
2019-04-24 20:56:53 +00:00
|
|
|
'vector-ataricolor': { //TODO
|
2019-12-19 00:47:40 +00:00
|
|
|
define: ['__VECTOR__'],
|
2019-04-24 20:56:53 +00:00
|
|
|
cfgfile: 'vector-color.cfg',
|
|
|
|
libargs: ['crt0.o', 'sim6502.lib'],
|
|
|
|
extra_link_files: ['crt0.o', 'vector-color.cfg'],
|
|
|
|
},
|
2017-04-02 18:54:51 +00:00
|
|
|
'sound_williams-z80': {
|
|
|
|
code_start: 0x0,
|
2017-05-02 13:09:53 +00:00
|
|
|
rom_size: 0x4000,
|
2017-04-02 18:54:51 +00:00
|
|
|
data_start: 0x4000,
|
2017-04-15 04:12:21 +00:00
|
|
|
data_size: 0x400,
|
2017-04-22 01:56:49 +00:00
|
|
|
stack_end: 0x8000,
|
2017-04-02 18:54:51 +00:00
|
|
|
},
|
2017-04-29 15:31:11 +00:00
|
|
|
'base_z80': {
|
|
|
|
code_start: 0x0,
|
2017-05-02 13:09:53 +00:00
|
|
|
rom_size: 0x8000,
|
2017-04-29 15:31:11 +00:00
|
|
|
data_start: 0x8000,
|
|
|
|
data_size: 0x8000,
|
|
|
|
stack_end: 0x0,
|
|
|
|
},
|
2017-05-01 11:37:48 +00:00
|
|
|
'coleco': {
|
2017-05-02 13:09:53 +00:00
|
|
|
rom_start: 0x8000,
|
|
|
|
code_start: 0x8100,
|
|
|
|
rom_size: 0x8000,
|
|
|
|
data_start: 0x7000,
|
2017-05-01 11:37:48 +00:00
|
|
|
data_size: 0x400,
|
|
|
|
stack_end: 0x8000,
|
2019-08-17 18:12:14 +00:00
|
|
|
extra_preproc_args: ['-I', '/share/include/coleco', '-D', 'CV_CV'],
|
2018-11-28 15:31:07 +00:00
|
|
|
extra_link_args: ['-k', '/share/lib/coleco', '-l', 'libcv', '-l', 'libcvu', 'crt0.rel'],
|
|
|
|
},
|
2019-08-17 18:12:14 +00:00
|
|
|
'msx': {
|
|
|
|
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'],
|
|
|
|
},
|
|
|
|
'msx-libcv': {
|
|
|
|
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'],
|
|
|
|
},
|
2018-11-29 22:40:53 +00:00
|
|
|
'sms-sg1000-libcv': {
|
2018-11-28 15:31:07 +00:00
|
|
|
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'],
|
2017-05-01 11:37:48 +00:00
|
|
|
},
|
2018-07-27 17:39:09 +00:00
|
|
|
'nes': { //TODO
|
2019-12-19 00:47:40 +00:00
|
|
|
define: ['__NES__'],
|
2019-08-21 15:14:30 +00:00
|
|
|
cfgfile: 'neslib2.cfg',
|
2019-05-14 16:01:54 +00:00
|
|
|
libargs: ['crt0.o', 'nes.lib', 'neslib2.lib',
|
2019-03-13 18:52:30 +00:00
|
|
|
'-D', 'NES_MAPPER=0', // NROM
|
|
|
|
'-D', 'NES_PRG_BANKS=2', // 2 16K PRG banks
|
2019-02-21 19:19:29 +00:00
|
|
|
'-D', 'NES_CHR_BANKS=1', // 1 CHR bank
|
2019-02-10 16:04:31 +00:00
|
|
|
'-D', 'NES_MIRRORING=0', // horizontal mirroring
|
2018-08-14 20:28:29 +00:00
|
|
|
],
|
2019-08-21 15:14:30 +00:00
|
|
|
extra_link_files: ['crt0.o', 'neslib2.lib', 'neslib2.cfg', 'nesbanked.cfg'],
|
2018-07-27 17:39:09 +00:00
|
|
|
},
|
2017-12-30 17:48:30 +00:00
|
|
|
'apple2': {
|
2019-12-19 00:47:40 +00:00
|
|
|
define: ['__APPLE2__'],
|
2018-06-20 07:09:37 +00:00
|
|
|
cfgfile: 'apple2-hgr.cfg',
|
2019-12-18 17:20:15 +00:00
|
|
|
libargs: [ '--lib-path', '/share/target/apple2/drv', '-D', '__EXEHDR__=0', 'apple2.lib'],
|
2018-08-06 20:23:19 +00:00
|
|
|
__CODE_RUN__: 16384,
|
2018-09-25 23:46:24 +00:00
|
|
|
code_start: 0x803,
|
2017-12-30 17:48:30 +00:00
|
|
|
},
|
2018-06-18 08:12:52 +00:00
|
|
|
'apple2-e': {
|
2019-12-19 00:47:40 +00:00
|
|
|
define: ['__APPLE2__'],
|
2018-06-18 08:12:52 +00:00
|
|
|
cfgfile: 'apple2.cfg',
|
|
|
|
libargs: ['apple2.lib'],
|
2018-06-20 08:06:18 +00:00
|
|
|
},
|
2020-07-28 20:06:49 +00:00
|
|
|
'atari8-800xl': {
|
2019-12-19 00:47:40 +00:00
|
|
|
define: ['__ATARI__'],
|
2018-06-22 06:24:52 +00:00
|
|
|
cfgfile: 'atari-cart.cfg',
|
2020-07-28 20:06:49 +00:00
|
|
|
libargs: ['atari.lib', '-D', '__CARTFLAGS__=4'],
|
2018-06-22 06:24:52 +00:00
|
|
|
},
|
2018-06-20 08:06:18 +00:00
|
|
|
'atari8-5200': {
|
2019-12-19 00:47:40 +00:00
|
|
|
define: ['__ATARI5200__'],
|
2018-06-21 18:15:05 +00:00
|
|
|
cfgfile: 'atari5200.cfg',
|
2020-07-28 20:06:49 +00:00
|
|
|
libargs: ['atari5200.lib', '-D', '__CARTFLAGS__=255'],
|
2018-06-22 06:24:52 +00:00
|
|
|
},
|
2017-11-11 19:45:32 +00:00
|
|
|
'verilog': {
|
|
|
|
},
|
2018-08-27 21:31:49 +00:00
|
|
|
'astrocade': {
|
|
|
|
code_start: 0x2000,
|
2018-09-03 13:37:17 +00:00
|
|
|
rom_size: 0x2000,
|
2018-09-02 14:48:43 +00:00
|
|
|
data_start: 0x4e10,
|
2018-09-03 13:37:17 +00:00
|
|
|
data_size: 0x1f0,
|
|
|
|
stack_end: 0x5000,
|
|
|
|
},
|
|
|
|
'astrocade-arcade': {
|
|
|
|
code_start: 0x0000,
|
|
|
|
rom_size: 0x4000,
|
|
|
|
data_start: 0x7de0,
|
|
|
|
data_size: 0x220,
|
|
|
|
stack_end: 0x8000,
|
2018-08-27 21:31:49 +00:00
|
|
|
},
|
2018-09-05 02:28:12 +00:00
|
|
|
'astrocade-bios': {
|
|
|
|
code_start: 0x0000,
|
|
|
|
rom_size: 0x2000,
|
|
|
|
data_start: 0x4fce,
|
|
|
|
data_size: 50,
|
|
|
|
stack_end: 0x4fce,
|
|
|
|
},
|
2019-08-06 03:47:23 +00:00
|
|
|
'atari7800': {
|
2019-12-19 00:47:40 +00:00
|
|
|
define: ['__ATARI7800__'],
|
2019-08-06 03:47:23 +00:00
|
|
|
cfgfile: 'atari7800.cfg',
|
2019-08-14 12:56:45 +00:00
|
|
|
libargs: ['crt0.o', 'sim6502.lib'],
|
|
|
|
extra_link_files: ['crt0.o', 'atari7800.cfg'],
|
2019-08-06 03:47:23 +00:00
|
|
|
},
|
2019-08-19 17:42:32 +00:00
|
|
|
'c64': {
|
2019-12-19 00:47:40 +00:00
|
|
|
define: ['__CBM__', '__C64__'],
|
2019-12-18 17:20:15 +00:00
|
|
|
cfgfile: 'c64.cfg', // SYS 2061
|
2019-08-19 17:42:32 +00:00
|
|
|
libargs: ['c64.lib'],
|
|
|
|
//extra_link_files: ['c64-cart.cfg'],
|
|
|
|
},
|
2019-08-21 01:34:01 +00:00
|
|
|
'kim1': {
|
|
|
|
},
|
2020-06-09 01:26:57 +00:00
|
|
|
'vectrex': {
|
|
|
|
code_start: 0x0,
|
|
|
|
rom_size: 0x8000,
|
|
|
|
data_start: 0xc880,
|
|
|
|
data_size: 0x380,
|
|
|
|
stack_end: 0xcc00,
|
2020-06-09 15:05:46 +00:00
|
|
|
extra_compile_files: ['assert.h','cmoc.h','stdarg.h','vectrex.h','stdlib.h','bios.h'],
|
2020-06-09 01:26:57 +00:00
|
|
|
extra_link_files: ['vectrex.scr', 'libcmoc-crt-vec.a', 'libcmoc-std-vec.a'],
|
2020-06-09 15:05:46 +00:00
|
|
|
extra_compile_args: ['--vectrex'],
|
2020-06-09 01:26:57 +00:00
|
|
|
extra_link_args: ['-svectrex.scr', '-lcmoc-crt-vec', '-lcmoc-std-vec'],
|
|
|
|
},
|
2020-06-14 01:28:58 +00:00
|
|
|
'x86': {
|
2020-07-01 23:16:38 +00:00
|
|
|
},
|
|
|
|
'zx': {
|
2020-07-02 17:33:22 +00:00
|
|
|
code_start: 0x5ccb,
|
|
|
|
rom_size: 0xff58-0x5ccb,
|
|
|
|
data_start: 0xf000,
|
|
|
|
data_size: 0xfe00-0xf000,
|
|
|
|
stack_end: 0xff58,
|
2020-07-20 17:00:25 +00:00
|
|
|
extra_link_args: ['crt0-zx.rel'],
|
|
|
|
extra_link_files: ['crt0-zx.rel', 'crt0-zx.lst'],
|
|
|
|
},
|
2020-10-17 19:34:08 +00:00
|
|
|
'devel-6502': {
|
|
|
|
cfgfile: 'devel-6502.cfg',
|
|
|
|
libargs: ['crt0.o', 'sim6502.lib'],
|
|
|
|
extra_link_files: ['crt0.o', 'devel-6502.cfg'],
|
|
|
|
},
|
2017-01-26 05:09:57 +00:00
|
|
|
};
|
|
|
|
|
2018-11-30 17:37:00 +00:00
|
|
|
PLATFORM_PARAMS['sms-sms-libcv'] = PLATFORM_PARAMS['sms-sg1000-libcv'];
|
2018-11-19 14:10:13 +00:00
|
|
|
|
2018-07-10 04:44:17 +00:00
|
|
|
var _t1;
|
2017-11-07 21:41:07 +00:00
|
|
|
function starttime() { _t1 = new Date(); }
|
2018-07-10 04:44:17 +00:00
|
|
|
function endtime(msg) { var _t2 = new Date(); console.log(msg, _t2.getTime() - _t1.getTime(), "ms"); }
|
2017-11-07 21:41:07 +00:00
|
|
|
|
2018-06-25 04:52:40 +00:00
|
|
|
/// working file store and build steps
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
type FileData = string | Uint8Array;
|
|
|
|
|
|
|
|
type FileEntry = {
|
|
|
|
path: string
|
|
|
|
encoding: string
|
|
|
|
data: FileData
|
|
|
|
ts: number
|
|
|
|
};
|
|
|
|
|
|
|
|
type BuildOptions = {
|
2018-12-15 18:14:40 +00:00
|
|
|
mainFilePath : string,
|
|
|
|
processFn?: (FileData) => FileData
|
2018-10-05 13:47:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
interface BuildStep extends WorkerBuildStep {
|
|
|
|
files? : string[]
|
|
|
|
args? : string[]
|
|
|
|
nextstep? : BuildStep
|
|
|
|
linkstep? : BuildStep
|
|
|
|
params?
|
|
|
|
result?
|
|
|
|
code?
|
|
|
|
prefix?
|
|
|
|
maxts?
|
|
|
|
};
|
|
|
|
|
|
|
|
var buildsteps : BuildStep[] = [];
|
|
|
|
var buildstartseq : number = 0;
|
|
|
|
var workfs : {[path:string]:FileEntry} = {};
|
|
|
|
var workerseq : number = 0;
|
2018-06-25 14:43:15 +00:00
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function compareData(a:FileData, b:FileData) : boolean {
|
2018-06-25 14:43:15 +00:00
|
|
|
if (a.length != b.length) return false;
|
2020-08-05 04:48:29 +00:00
|
|
|
if (typeof a === 'string' && typeof b === 'string') {
|
|
|
|
return a == b;
|
|
|
|
} else {
|
2018-06-25 14:43:15 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2018-06-25 04:52:40 +00:00
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function putWorkFile(path:string, data:FileData) {
|
2018-06-25 04:52:40 +00:00
|
|
|
var encoding = (typeof data === 'string') ? 'utf8' : 'binary';
|
|
|
|
var entry = workfs[path];
|
2018-06-25 14:43:15 +00:00
|
|
|
if (!entry || !compareData(entry.data, data) || entry.encoding != encoding) {
|
2018-06-28 03:26:23 +00:00
|
|
|
workfs[path] = entry = {path:path, data:data, encoding:encoding, ts:++workerseq};
|
2018-06-25 14:43:15 +00:00
|
|
|
console.log('+++', entry.path, entry.encoding, entry.data.length, entry.ts);
|
2018-06-25 04:52:40 +00:00
|
|
|
}
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
2018-06-28 03:26:23 +00:00
|
|
|
// returns true if file changed during this build step
|
2018-10-05 13:47:15 +00:00
|
|
|
function wasChanged(entry:FileEntry) : boolean {
|
2018-06-28 03:26:23 +00:00
|
|
|
return entry.ts > buildstartseq;
|
|
|
|
}
|
|
|
|
|
2018-11-28 16:10:24 +00:00
|
|
|
function getWorkFileAsString(path:string) : string {
|
|
|
|
return workfs[path] && workfs[path].data as string; // TODO
|
|
|
|
}
|
|
|
|
|
2018-12-15 18:14:40 +00:00
|
|
|
function populateEntry(fs, path:string, entry:FileEntry, options:BuildOptions) {
|
|
|
|
var data = entry.data;
|
|
|
|
if (options && options.processFn)
|
|
|
|
data = options.processFn(data);
|
2019-04-26 19:38:34 +00:00
|
|
|
// 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
|
2018-12-15 18:14:40 +00:00
|
|
|
fs.writeFile(path, data, {encoding:entry.encoding});
|
2018-06-25 14:43:15 +00:00
|
|
|
fs.utime(path, entry.ts, entry.ts);
|
2018-06-28 00:26:22 +00:00
|
|
|
console.log("<<<", path, entry.data.length);
|
2018-06-25 14:43:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// can call multiple times (from populateFiles)
|
2018-10-05 13:47:15 +00:00
|
|
|
function gatherFiles(step:BuildStep, options?:BuildOptions) {
|
2018-06-25 14:43:15 +00:00
|
|
|
var maxts = 0;
|
|
|
|
if (step.files) {
|
|
|
|
for (var i=0; i<step.files.length; i++) {
|
|
|
|
var path = step.files[i];
|
|
|
|
var entry = workfs[path];
|
2018-12-15 18:14:40 +00:00
|
|
|
if (!entry) {
|
|
|
|
throw new Error("No entry for path '" + path + "'");
|
|
|
|
} else {
|
|
|
|
maxts = Math.max(maxts, entry.ts);
|
|
|
|
}
|
2018-06-25 14:43:15 +00:00
|
|
|
}
|
|
|
|
}
|
2018-06-28 00:26:22 +00:00
|
|
|
else if (step.code) {
|
2018-10-05 13:47:15 +00:00
|
|
|
var path = step.path ? step.path : options.mainFilePath; // TODO: what if options null
|
2019-11-13 20:45:18 +00:00
|
|
|
if (!path) throw Error("need path or mainFilePath");
|
2018-06-25 04:52:40 +00:00
|
|
|
var code = step.code;
|
|
|
|
var entry = putWorkFile(path, code);
|
|
|
|
step.path = path;
|
2018-06-25 14:43:15 +00:00
|
|
|
step.files = [path];
|
|
|
|
maxts = entry.ts;
|
2018-06-25 04:52:40 +00:00
|
|
|
}
|
|
|
|
else if (step.path) {
|
|
|
|
var path = step.path;
|
|
|
|
var entry = workfs[path];
|
2018-06-25 14:43:15 +00:00
|
|
|
maxts = entry.ts;
|
|
|
|
step.files = [path];
|
2018-06-25 04:52:40 +00:00
|
|
|
}
|
|
|
|
if (step.path && !step.prefix) {
|
2019-05-04 00:13:08 +00:00
|
|
|
step.prefix = step.path;
|
|
|
|
var pos = step.prefix.lastIndexOf('.');
|
|
|
|
if (pos > 0)
|
|
|
|
step.prefix = step.prefix.substring(0, pos);
|
2018-06-25 04:52:40 +00:00
|
|
|
}
|
2018-06-25 14:43:15 +00:00
|
|
|
step.maxts = maxts;
|
|
|
|
return maxts;
|
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function populateFiles(step:BuildStep, fs, options?:BuildOptions) {
|
2018-06-25 14:43:15 +00:00
|
|
|
gatherFiles(step, options);
|
2019-11-13 20:45:18 +00:00
|
|
|
if (!step.files) throw Error("call gatherFiles() first");
|
2018-06-25 14:43:15 +00:00
|
|
|
for (var i=0; i<step.files.length; i++) {
|
|
|
|
var path = step.files[i];
|
2018-12-15 18:14:40 +00:00
|
|
|
populateEntry(fs, path, workfs[path], options);
|
2018-06-25 14:43:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-28 15:31:07 +00:00
|
|
|
function populateExtraFiles(step:BuildStep, fs, extrafiles) {
|
2018-08-14 20:28:29 +00:00
|
|
|
if (extrafiles) {
|
|
|
|
for (var i=0; i<extrafiles.length; i++) {
|
|
|
|
var xfn = extrafiles[i];
|
2019-08-23 00:53:48 +00:00
|
|
|
// is this file cached?
|
|
|
|
if (workfs[xfn]) {
|
|
|
|
fs.writeFile(xfn, workfs[xfn].data, {encoding:'binary'});
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// fetch from network
|
2019-04-03 21:00:05 +00:00
|
|
|
var xpath = "lib/" + getBasePlatform(step.platform) + "/" + xfn;
|
2018-08-14 20:28:29 +00:00
|
|
|
var xhr = new XMLHttpRequest();
|
|
|
|
xhr.responseType = 'arraybuffer';
|
|
|
|
xhr.open("GET", 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'});
|
2019-08-23 00:53:48 +00:00
|
|
|
putWorkFile(xfn, data);
|
2018-08-15 04:43:52 +00:00
|
|
|
console.log(":::",xfn,data.length);
|
2018-08-14 20:28:29 +00:00
|
|
|
} else {
|
|
|
|
throw Error("Could not load extra file " + xpath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function staleFiles(step:BuildStep, targets:string[]) {
|
2019-11-13 20:45:18 +00:00
|
|
|
if (!step.maxts) throw Error("call populateFiles() first");
|
2018-06-28 03:26:23 +00:00
|
|
|
// see if any target files are more recent than inputs
|
2018-06-25 14:43:15 +00:00
|
|
|
for (var i=0; i<targets.length; i++) {
|
|
|
|
var entry = workfs[targets[i]];
|
|
|
|
if (!entry || step.maxts > entry.ts)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
console.log("unchanged", step.maxts, targets);
|
|
|
|
return false;
|
2018-06-25 04:52:40 +00:00
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function anyTargetChanged(step:BuildStep, targets:string[]) {
|
2019-11-13 20:45:18 +00:00
|
|
|
if (!step.maxts) throw Error("call populateFiles() first");
|
2018-06-28 04:57:06 +00:00
|
|
|
// see if any target files are more recent than inputs
|
|
|
|
for (var i=0; i<targets.length; i++) {
|
|
|
|
var entry = workfs[targets[i]];
|
|
|
|
if (!entry || entry.ts > step.maxts)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
console.log("unchanged", step.maxts, targets);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function execMain(step:BuildStep, mod, args:string[]) {
|
2018-06-25 04:52:40 +00:00
|
|
|
starttime();
|
2020-06-08 21:36:33 +00:00
|
|
|
var run = mod.callMain || mod.run; // TODO: run?
|
2019-12-07 23:20:01 +00:00
|
|
|
run(args);
|
2018-06-25 04:52:40 +00:00
|
|
|
endtime(step.tool);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// asm.js / WASM / filesystem loading
|
|
|
|
|
2017-01-23 21:21:45 +00:00
|
|
|
var fsMeta = {};
|
|
|
|
var fsBlob = {};
|
2017-11-05 18:34:00 +00:00
|
|
|
var wasmBlob = {};
|
2017-01-23 21:21:45 +00:00
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
const PSRC = "../../src/";
|
|
|
|
const PWORKER = PSRC+"worker/";
|
|
|
|
|
2017-01-10 08:18:54 +00:00
|
|
|
// load filesystems for CC65 and others asynchronously
|
2018-10-05 13:47:15 +00:00
|
|
|
function loadFilesystem(name:string) {
|
2017-01-06 00:14:12 +00:00
|
|
|
var xhr = new XMLHttpRequest();
|
|
|
|
xhr.responseType = 'blob';
|
2018-10-05 13:47:15 +00:00
|
|
|
xhr.open("GET", PWORKER+"fs/fs"+name+".data", false); // synchronous request
|
2017-01-06 00:14:12 +00:00
|
|
|
xhr.send(null);
|
2017-01-23 21:21:45 +00:00
|
|
|
fsBlob[name] = xhr.response;
|
2017-01-06 00:14:12 +00:00
|
|
|
xhr = new XMLHttpRequest();
|
|
|
|
xhr.responseType = 'json';
|
2018-10-05 13:47:15 +00:00
|
|
|
xhr.open("GET", PWORKER+"fs/fs"+name+".js.metadata", false); // synchronous request
|
2017-01-06 00:14:12 +00:00
|
|
|
xhr.send(null);
|
2017-01-23 21:21:45 +00:00
|
|
|
fsMeta[name] = xhr.response;
|
|
|
|
console.log("Loaded "+name+" filesystem", fsMeta[name].files.length, 'files', fsBlob[name].size, 'bytes');
|
2017-01-06 00:14:12 +00:00
|
|
|
}
|
|
|
|
|
2017-11-05 18:34:00 +00:00
|
|
|
var loaded = {}
|
2018-10-05 13:47:15 +00:00
|
|
|
function load(modulename:string, debug?:boolean) {
|
2017-11-05 18:34:00 +00:00
|
|
|
if (!loaded[modulename]) {
|
2018-10-05 13:47:15 +00:00
|
|
|
importScripts(PWORKER+'asmjs/'+modulename+(debug?"."+debug+".js":".js"));
|
2017-11-05 18:34:00 +00:00
|
|
|
loaded[modulename] = 1;
|
|
|
|
}
|
|
|
|
}
|
2018-10-05 13:47:15 +00:00
|
|
|
function loadGen(modulename:string) {
|
2018-07-11 04:04:28 +00:00
|
|
|
if (!loaded[modulename]) {
|
|
|
|
importScripts('../../gen/'+modulename+".js");
|
|
|
|
loaded[modulename] = 1;
|
|
|
|
}
|
|
|
|
}
|
2018-10-05 13:47:15 +00:00
|
|
|
function loadWASM(modulename:string, debug?:boolean) {
|
2017-11-05 18:34:00 +00:00
|
|
|
if (!loaded[modulename]) {
|
2018-10-05 13:47:15 +00:00
|
|
|
importScripts(PWORKER+"wasm/" + modulename+(debug?"."+debug+".js":".js"));
|
2017-11-05 18:34:00 +00:00
|
|
|
var xhr = new XMLHttpRequest();
|
|
|
|
xhr.responseType = 'arraybuffer';
|
2018-10-05 13:47:15 +00:00
|
|
|
xhr.open("GET", PWORKER+"wasm/"+modulename+".wasm", false); // synchronous request
|
2017-11-05 18:34:00 +00:00
|
|
|
xhr.send(null);
|
|
|
|
if (xhr.response) {
|
2018-07-10 04:44:17 +00:00
|
|
|
wasmBlob[modulename] = new Uint8Array(xhr.response);
|
2019-03-14 15:20:50 +00:00
|
|
|
console.log("Loaded " + modulename + ".wasm (" + wasmBlob[modulename].length + " bytes)");
|
2017-11-05 18:34:00 +00:00
|
|
|
loaded[modulename] = 1;
|
|
|
|
} else {
|
2018-03-03 02:41:56 +00:00
|
|
|
throw Error("Could not load WASM file " + modulename + ".wasm");
|
2017-11-05 18:34:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-05 13:47:15 +00:00
|
|
|
function loadNative(modulename:string) {
|
2017-11-07 21:41:07 +00:00
|
|
|
// detect WASM
|
2018-07-10 05:09:10 +00:00
|
|
|
if (CACHE_WASM_MODULES && typeof WebAssembly === 'object') {
|
2017-11-07 21:41:07 +00:00
|
|
|
loadWASM(modulename);
|
|
|
|
} else {
|
|
|
|
load(modulename);
|
|
|
|
}
|
|
|
|
}
|
2017-11-05 18:34:00 +00:00
|
|
|
|
2017-01-10 08:18:54 +00:00
|
|
|
// mount the filesystem at /share
|
2018-10-05 13:47:15 +00:00
|
|
|
function setupFS(FS, name:string) {
|
2019-03-13 18:52:30 +00:00
|
|
|
var WORKERFS = FS.filesystems['WORKERFS'];
|
2019-04-24 20:56:53 +00:00
|
|
|
if (name === '65-vector') name = '65-sim6502'; // TODO
|
2019-08-14 12:56:45 +00:00
|
|
|
if (name === '65-atari7800') name = '65-sim6502'; // TODO
|
2020-10-17 19:34:08 +00:00
|
|
|
if (name === '65-devel') name = '65-sim6502'; // TODO
|
2019-11-13 20:45:18 +00:00
|
|
|
if (!fsMeta[name]) throw Error("No filesystem for '" + name + "'");
|
2017-01-06 00:14:12 +00:00
|
|
|
FS.mkdir('/share');
|
2018-08-06 15:20:55 +00:00
|
|
|
FS.mount(WORKERFS, {
|
2017-01-23 21:21:45 +00:00
|
|
|
packages: [{ metadata: fsMeta[name], blob: fsBlob[name] }]
|
2017-01-06 00:14:12 +00:00
|
|
|
}, '/share');
|
2018-08-06 15:20:55 +00:00
|
|
|
// fix for slow Blob operations by caching typed arrays
|
|
|
|
// https://github.com/kripken/emscripten/blob/incoming/src/library_workerfs.js
|
2019-08-09 15:42:36 +00:00
|
|
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=349304#c30
|
2018-08-06 15:20:55 +00:00
|
|
|
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;
|
|
|
|
};
|
2017-01-06 00:14:12 +00:00
|
|
|
}
|
2016-12-16 01:21:51 +00:00
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
var print_fn = function(s:string) {
|
2017-01-12 16:22:27 +00:00
|
|
|
console.log(s);
|
|
|
|
//console.log(new Error().stack);
|
|
|
|
}
|
2017-01-06 00:14:12 +00:00
|
|
|
|
2017-01-13 02:21:35 +00:00
|
|
|
// test.c(6) : warning 85: in function main unreferenced local variable : 'x'
|
2017-01-16 15:35:19 +00:00
|
|
|
// main.a (4): error: Unknown Mnemonic 'xxx'.
|
2017-01-22 14:35:04 +00:00
|
|
|
// at 2: warning 190: ISO C forbids an empty source file
|
2018-08-18 00:46:55 +00:00
|
|
|
var re_msvc = /[/]*([^( ]+)\s*[(](\d+)[)]\s*:\s*(.+?):\s*(.*)/;
|
2017-01-16 15:35:19 +00:00
|
|
|
var re_msvc2 = /\s*(at)\s+(\d+)\s*(:)\s*(.*)/;
|
2018-06-19 22:42:02 +00:00
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function msvcErrorMatcher(errors:WorkerError[]) {
|
|
|
|
return function(s:string) {
|
2018-06-19 22:42:02 +00:00
|
|
|
var matches = re_msvc.exec(s) || re_msvc2.exec(s);
|
|
|
|
if (matches) {
|
|
|
|
var errline = parseInt(matches[2]);
|
|
|
|
errors.push({
|
|
|
|
line:errline,
|
2018-08-18 00:46:55 +00:00
|
|
|
path:matches[1],
|
2018-10-05 13:47:15 +00:00
|
|
|
//type:matches[3],
|
2018-06-19 22:42:02 +00:00
|
|
|
msg:matches[4]
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
console.log(s);
|
|
|
|
}
|
2017-01-13 02:21:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-15 18:14:40 +00:00
|
|
|
function makeErrorMatcher(errors:WorkerError[], regex, iline:number, imsg:number, mainpath:string, ifilename?:number) {
|
2017-01-23 21:21:45 +00:00
|
|
|
return function(s) {
|
|
|
|
var matches = regex.exec(s);
|
|
|
|
if (matches) {
|
|
|
|
errors.push({
|
2017-11-13 05:24:19 +00:00
|
|
|
line:parseInt(matches[iline]) || 1,
|
2018-08-18 00:46:55 +00:00
|
|
|
msg:matches[imsg],
|
2018-12-15 18:14:40 +00:00
|
|
|
path:ifilename ? matches[ifilename] : mainpath
|
2017-01-23 21:21:45 +00:00
|
|
|
});
|
2017-11-13 05:24:19 +00:00
|
|
|
} else {
|
2018-07-03 03:55:38 +00:00
|
|
|
console.log("??? "+s);
|
2017-01-23 21:21:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-23 00:26:47 +00:00
|
|
|
function extractErrors(regex, strings:string[], path:string, iline, imsg, ifilename) {
|
2017-01-23 21:21:45 +00:00
|
|
|
var errors = [];
|
2019-03-23 00:26:47 +00:00
|
|
|
var matcher = makeErrorMatcher(errors, regex, iline, imsg, path, ifilename);
|
2017-01-23 21:21:45 +00:00
|
|
|
for (var i=0; i<strings.length; i++) {
|
|
|
|
matcher(strings[i]);
|
|
|
|
}
|
|
|
|
return errors;
|
|
|
|
}
|
|
|
|
|
2018-07-30 02:33:15 +00:00
|
|
|
// TODO: "of" doesn't work in MSIE
|
|
|
|
|
2018-08-28 17:59:12 +00:00
|
|
|
var re_crlf = /\r?\n/;
|
2020-07-10 18:25:34 +00:00
|
|
|
// 1 %line 16+1 hello.asm
|
|
|
|
var re_lineoffset = /\s*(\d+)\s+[%]line\s+(\d+)\+(\d+)\s+(.+)/;
|
2018-08-28 17:59:12 +00:00
|
|
|
|
2019-06-11 13:39:45 +00:00
|
|
|
function parseListing(code:string, lineMatch, iline:number, ioffset:number, iinsns:number, icycles?:number) : SourceLine[] {
|
|
|
|
var lines : SourceLine[] = [];
|
2020-07-10 18:25:34 +00:00
|
|
|
var lineofs = 0;
|
2020-07-06 23:53:20 +00:00
|
|
|
code.split(re_crlf).forEach((line, lineindex) => {
|
2017-01-14 02:31:04 +00:00
|
|
|
var linem = lineMatch.exec(line);
|
|
|
|
if (linem && linem[1]) {
|
2020-07-06 23:53:20 +00:00
|
|
|
var linenum = iline < 0 ? lineindex : parseInt(linem[iline]);
|
2017-01-15 03:46:12 +00:00
|
|
|
var offset = parseInt(linem[ioffset], 16);
|
|
|
|
var insns = linem[iinsns];
|
2019-06-11 13:39:45 +00:00
|
|
|
var cycles : number = icycles ? parseInt(linem[icycles]) : null;
|
|
|
|
var iscode = cycles > 0;
|
2017-01-14 02:31:04 +00:00
|
|
|
if (insns) {
|
|
|
|
lines.push({
|
2020-07-10 18:25:34 +00:00
|
|
|
line:linenum + lineofs,
|
2018-08-29 12:24:13 +00:00
|
|
|
offset:offset,
|
2017-01-14 02:31:04 +00:00
|
|
|
insns:insns,
|
2019-06-11 13:39:45 +00:00
|
|
|
cycles:cycles,
|
|
|
|
iscode:iscode
|
2017-01-14 02:31:04 +00:00
|
|
|
});
|
|
|
|
}
|
2020-07-10 18:25:34 +00:00
|
|
|
} else {
|
|
|
|
let m = re_lineoffset.exec(line);
|
|
|
|
// TODO: check filename too
|
|
|
|
if (m) {
|
|
|
|
lineofs = parseInt(m[2]) - parseInt(m[1]) - parseInt(m[3]);
|
|
|
|
}
|
2017-01-14 02:31:04 +00:00
|
|
|
}
|
2020-07-06 23:53:20 +00:00
|
|
|
});
|
2017-01-14 02:31:04 +00:00
|
|
|
return lines;
|
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function parseSourceLines(code:string, lineMatch, offsetMatch) {
|
2017-01-15 18:31:52 +00:00
|
|
|
var lines = [];
|
|
|
|
var lastlinenum = 0;
|
2018-08-28 17:59:12 +00:00
|
|
|
for (var line of code.split(re_crlf)) {
|
2017-01-15 18:31:52 +00:00
|
|
|
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,
|
2018-08-29 12:24:13 +00:00
|
|
|
offset:offset,
|
2017-01-15 18:31:52 +00:00
|
|
|
});
|
|
|
|
lastlinenum = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return lines;
|
|
|
|
}
|
|
|
|
|
2019-12-27 22:31:24 +00:00
|
|
|
function parseDASMListing(code:string, listings:CodeListingMap, errors:WorkerError[], unresolved:{}) {
|
2019-05-11 13:54:09 +00:00
|
|
|
// TODO: this gets very slow
|
2016-12-16 01:21:51 +00:00
|
|
|
// 4 08ee a9 00 start lda #01workermain.js:23:5
|
2018-08-25 02:55:16 +00:00
|
|
|
var lineMatch = /\s*(\d+)\s+(\S+)\s+([0-9a-f]+)\s+([?0-9a-f][?0-9a-f ]+)?\s+(.+)?/i;
|
|
|
|
var equMatch = /\bequ\b/i;
|
|
|
|
var macroMatch = /\bMAC\s+(.+)?/i;
|
|
|
|
var macrolines = [];
|
2016-12-16 01:21:51 +00:00
|
|
|
var lastline = 0;
|
2018-08-25 02:55:16 +00:00
|
|
|
var macros = {};
|
2018-08-28 17:59:12 +00:00
|
|
|
for (var line of code.split(re_crlf)) {
|
2016-12-16 01:21:51 +00:00
|
|
|
var linem = lineMatch.exec(line);
|
|
|
|
if (linem && linem[1]) {
|
2018-07-02 13:34:20 +00:00
|
|
|
var linenum = parseInt(linem[1]);
|
2016-12-16 01:21:51 +00:00
|
|
|
var filename = linem[2];
|
|
|
|
var offset = parseInt(linem[3], 16);
|
|
|
|
var insns = linem[4];
|
|
|
|
var restline = linem[5];
|
2018-08-25 02:55:16 +00:00
|
|
|
if (insns && insns.startsWith('?')) insns = null;
|
2019-05-11 19:31:09 +00:00
|
|
|
// inside of a file?
|
|
|
|
var lst = listings[filename];
|
|
|
|
if (lst) {
|
|
|
|
var lines = lst.lines;
|
2018-08-25 02:55:16 +00:00
|
|
|
// look for MAC statement
|
|
|
|
var macmatch = macroMatch.exec(restline);
|
|
|
|
if (macmatch) {
|
|
|
|
macros[macmatch[1]] = {line:parseInt(linem[1]), file:linem[2].toLowerCase()};
|
|
|
|
}
|
|
|
|
else if (insns && !restline.match(equMatch)) {
|
2016-12-16 01:21:51 +00:00
|
|
|
lines.push({
|
|
|
|
line:linenum,
|
|
|
|
offset:offset,
|
|
|
|
insns:insns,
|
|
|
|
iscode:restline[0] != '.'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
lastline = linenum;
|
|
|
|
} else {
|
|
|
|
// inside of macro or include file
|
2017-01-16 15:35:19 +00:00
|
|
|
if (insns && linem[3] && lastline>0) {
|
2016-12-16 01:21:51 +00:00
|
|
|
lines.push({
|
|
|
|
line:lastline+1,
|
|
|
|
offset:offset,
|
|
|
|
insns:null
|
|
|
|
});
|
|
|
|
}
|
2018-08-25 02:55:16 +00:00
|
|
|
// inside of macro?
|
|
|
|
var mac = macros[filename.toLowerCase()];
|
|
|
|
if (insns && mac) {
|
|
|
|
macrolines.push({
|
|
|
|
filename:mac.file,
|
|
|
|
line:mac.line+linenum,
|
|
|
|
offset:offset,
|
|
|
|
insns:insns
|
|
|
|
});
|
|
|
|
}
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
// TODO: better symbol test (word boundaries)
|
2018-08-26 02:00:45 +00:00
|
|
|
// TODO: ignore IFCONST and IFNCONST usage
|
2017-01-03 15:43:40 +00:00
|
|
|
for (var key in unresolved) {
|
2019-05-11 13:54:09 +00:00
|
|
|
var l = restline || line;
|
|
|
|
var pos = l.indexOf(key);
|
2016-12-16 01:21:51 +00:00
|
|
|
if (pos >= 0) {
|
2019-05-11 13:54:09 +00:00
|
|
|
var cmt = l.indexOf(';');
|
|
|
|
if (cmt < 0 || cmt > pos) {
|
2019-05-11 19:31:09 +00:00
|
|
|
// make sure identifier is flanked by non-word chars
|
|
|
|
if (/\w+/.test(key) && new RegExp("\\b"+key+"\\b").test(key)) {
|
|
|
|
errors.push({
|
|
|
|
path:filename,
|
|
|
|
line:linenum,
|
|
|
|
msg:"Unresolved symbol '" + key + "'"
|
|
|
|
});
|
|
|
|
}
|
2019-05-11 13:54:09 +00:00
|
|
|
}
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-01-16 15:35:19 +00:00
|
|
|
var errm = re_msvc.exec(line);
|
2016-12-16 01:21:51 +00:00
|
|
|
if (errm) {
|
|
|
|
errors.push({
|
2018-08-18 00:46:55 +00:00
|
|
|
path:errm[1],
|
2017-01-16 15:35:19 +00:00
|
|
|
line:parseInt(errm[2]),
|
|
|
|
msg:errm[4]
|
2016-12-16 01:21:51 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2018-08-25 02:55:16 +00:00
|
|
|
// TODO: use macrolines
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function assembleDASM(step:BuildStep) {
|
2017-01-14 02:31:04 +00:00
|
|
|
load("dasm");
|
2016-12-16 01:21:51 +00:00
|
|
|
var re_usl = /(\w+)\s+0000\s+[?][?][?][?]/;
|
|
|
|
var unresolved = {};
|
2018-08-03 18:06:08 +00:00
|
|
|
var errors = [];
|
2018-10-05 13:47:15 +00:00
|
|
|
var errorMatcher = msvcErrorMatcher(errors);
|
2017-01-06 00:14:12 +00:00
|
|
|
function match_fn(s) {
|
2016-12-16 01:21:51 +00:00
|
|
|
var matches = re_usl.exec(s);
|
|
|
|
if (matches) {
|
2018-08-26 02:00:45 +00:00
|
|
|
var key = matches[1];
|
|
|
|
if (key != 'NO_ILLEGAL_OPCODES') { // TODO
|
|
|
|
unresolved[matches[1]] = 0;
|
|
|
|
}
|
2018-08-03 18:06:08 +00:00
|
|
|
} else if (s.startsWith("Warning:")) {
|
2018-09-05 02:28:12 +00:00
|
|
|
errors.push({line:0, msg:s.substr(9)});
|
2019-03-02 23:15:03 +00:00
|
|
|
} else if (s.startsWith("unable ")) {
|
|
|
|
errors.push({line:0, msg:s});
|
2018-10-05 13:47:15 +00:00
|
|
|
} else {
|
|
|
|
errorMatcher(s);
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-05 13:47:15 +00:00
|
|
|
var Module = emglobal.DASM({
|
2016-12-16 01:21:51 +00:00
|
|
|
noInitialRun:true,
|
2017-01-06 00:14:12 +00:00
|
|
|
print:match_fn
|
2016-12-16 01:21:51 +00:00
|
|
|
});
|
|
|
|
var FS = Module['FS'];
|
2018-06-25 04:52:40 +00:00
|
|
|
populateFiles(step, FS, {
|
2018-07-02 13:34:20 +00:00
|
|
|
mainFilePath:'main.a'
|
2018-06-25 04:52:40 +00:00
|
|
|
});
|
2018-06-28 00:26:22 +00:00
|
|
|
var binpath = step.prefix+'.bin';
|
|
|
|
var lstpath = step.prefix+'.lst';
|
2018-07-02 13:34:20 +00:00
|
|
|
var sympath = step.prefix+'.sym';
|
2018-11-22 16:22:54 +00:00
|
|
|
execMain(step, Module, [step.path, '-f3',
|
2018-11-20 17:31:19 +00:00
|
|
|
"-l"+lstpath,
|
|
|
|
"-o"+binpath,
|
|
|
|
"-s"+sympath ]);
|
2018-06-28 00:26:22 +00:00
|
|
|
var alst = FS.readFile(lstpath, {'encoding':'utf8'});
|
2019-05-11 19:31:09 +00:00
|
|
|
// parse main listing, get errors and listings for each file
|
2019-12-27 22:31:24 +00:00
|
|
|
var listings : CodeListingMap = {};
|
2019-05-11 19:31:09 +00:00
|
|
|
for (let path of step.files) {
|
|
|
|
listings[path] = {lines:[]};
|
2018-08-04 14:21:50 +00:00
|
|
|
}
|
2019-05-11 19:31:09 +00:00
|
|
|
parseDASMListing(alst, listings, errors, unresolved);
|
2019-05-22 01:39:37 +00:00
|
|
|
if (errors.length) {
|
|
|
|
return {errors:errors};
|
|
|
|
}
|
2019-05-11 19:31:09 +00:00
|
|
|
// read binary rom output and symbols
|
2018-11-20 19:39:33 +00:00
|
|
|
var aout, asym;
|
|
|
|
aout = FS.readFile(binpath);
|
|
|
|
try {
|
|
|
|
asym = FS.readFile(sympath, {'encoding':'utf8'});
|
|
|
|
} catch (e) {
|
|
|
|
console.log(e);
|
|
|
|
errors.push({line:0,msg:"No symbol table generated, maybe segment overflow?"});
|
|
|
|
return {errors:errors}
|
|
|
|
}
|
2018-06-28 00:26:22 +00:00
|
|
|
putWorkFile(binpath, aout);
|
|
|
|
putWorkFile(lstpath, alst);
|
2018-07-02 13:34:20 +00:00
|
|
|
putWorkFile(sympath, asym);
|
2018-07-04 02:14:07 +00:00
|
|
|
// return unchanged if no files changed
|
|
|
|
// TODO: what if listing or symbols change?
|
|
|
|
if (!anyTargetChanged(step, [binpath/*, lstpath, sympath*/]))
|
|
|
|
return;
|
2018-07-02 13:34:20 +00:00
|
|
|
var symbolmap = {};
|
|
|
|
for (var s of asym.split("\n")) {
|
2018-08-19 13:27:00 +00:00
|
|
|
var toks = s.split(/\s+/);
|
2018-07-02 13:34:20 +00:00
|
|
|
if (toks && toks.length >= 2 && !toks[0].startsWith('-')) {
|
|
|
|
symbolmap[toks[0]] = parseInt(toks[1], 16);
|
|
|
|
}
|
|
|
|
}
|
2018-11-21 12:21:07 +00:00
|
|
|
// for bataribasic (TODO)
|
|
|
|
if (step['bblines']) {
|
2019-05-14 17:56:48 +00:00
|
|
|
let lst = listings[step.path];
|
|
|
|
if (lst) {
|
|
|
|
lst.asmlines = lst.lines;
|
|
|
|
lst.text = alst;
|
|
|
|
lst.lines = [];
|
|
|
|
}
|
2018-11-21 12:21:07 +00:00
|
|
|
}
|
2017-01-10 08:18:54 +00:00
|
|
|
return {
|
2018-11-22 16:22:54 +00:00
|
|
|
output:aout,
|
2018-06-28 00:26:22 +00:00
|
|
|
listings:listings,
|
2018-08-03 18:06:08 +00:00
|
|
|
errors:errors,
|
2018-07-02 13:34:20 +00:00
|
|
|
symbolmap:symbolmap,
|
2017-01-10 08:18:54 +00:00
|
|
|
};
|
2017-01-04 21:07:59 +00:00
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function setupStdin(fs, code:string) {
|
2017-01-13 02:21:35 +00:00
|
|
|
var i = 0;
|
|
|
|
fs.init(
|
|
|
|
function() { return i<code.length ? code.charCodeAt(i++) : null; }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-07-06 04:50:11 +00:00
|
|
|
//TODO: this doesn't align very well
|
2018-06-25 23:47:40 +00:00
|
|
|
/*
|
2020-07-06 04:50:11 +00:00
|
|
|
000000r 1 .segment "CODE"
|
|
|
|
000000r 1 .proc _rasterWait: near
|
|
|
|
000000r 1 ; int main() { return mul2(2); }
|
|
|
|
000000r 1 .dbg line, "main.c", 3
|
|
|
|
000014r 1 .dbg func, "main", "00", extern, "_main"
|
|
|
|
000000r 1 A2 00 ldx #$00
|
|
|
|
00B700 1 BOOT2:
|
|
|
|
00B700 1 A2 01 ldx #1 ;track
|
|
|
|
00B725 1 00 IBLASTDRVN: .byte 0
|
|
|
|
00B726 1 xx xx IBSECSZ: .res 2
|
|
|
|
00BA2F 1 2A 2B E8 2C HEX "2A2BE82C2D2E2F303132F0F133343536"
|
2018-06-25 23:47:40 +00:00
|
|
|
*/
|
2018-08-06 20:23:19 +00:00
|
|
|
function parseCA65Listing(code, symbols, params, dbg) {
|
2018-06-25 23:47:40 +00:00
|
|
|
var segofs = 0;
|
2019-05-04 02:08:43 +00:00
|
|
|
var offset = 0;
|
|
|
|
var dbgLineMatch = /^([0-9A-F]+)([r]?)\s+(\d+)\s+[.]dbg\s+(\w+), "([^"]+)", (.+)/;
|
|
|
|
var funcLineMatch = /"(\w+)", (\w+), "(\w+)"/;
|
2020-07-06 04:50:11 +00:00
|
|
|
var insnLineMatch = /^([0-9A-F]+)([r]?)\s{1,2}(\d+)\s{1,2}([0-9A-Frx ]{11})\s+(.*)/;
|
2017-01-06 00:14:12 +00:00
|
|
|
var lines = [];
|
2020-07-06 04:50:11 +00:00
|
|
|
var linenum = 0;
|
2019-05-04 02:08:43 +00:00
|
|
|
// TODO: only does .c functions, not all .s files
|
2018-08-28 17:59:12 +00:00
|
|
|
for (var line of code.split(re_crlf)) {
|
2019-12-28 19:49:09 +00:00
|
|
|
var dbgm = dbgLineMatch.exec(line);
|
|
|
|
if (dbgm && dbgm[1]) {
|
|
|
|
var dbgtype = dbgm[4];
|
|
|
|
offset = parseInt(dbgm[1], 16);
|
|
|
|
if (dbgtype == 'func') {
|
|
|
|
var funcm = funcLineMatch.exec(dbgm[6]);
|
|
|
|
if (funcm) {
|
|
|
|
var funcofs = symbols[funcm[3]];
|
|
|
|
if (typeof funcofs === 'number') {
|
|
|
|
segofs = funcofs - offset;
|
|
|
|
//console.log(funcm[3], funcofs, '-', offset);
|
2019-05-04 02:08:43 +00:00
|
|
|
}
|
|
|
|
}
|
2018-06-25 23:47:40 +00:00
|
|
|
}
|
2019-12-28 19:49:09 +00:00
|
|
|
}
|
|
|
|
if (dbg) {
|
|
|
|
if (dbgm && dbgtype == 'line') {
|
|
|
|
lines.push({
|
|
|
|
// TODO: sourcefile
|
|
|
|
line:parseInt(dbgm[6]),
|
|
|
|
offset:offset + segofs,
|
|
|
|
insns:null
|
|
|
|
});
|
|
|
|
}
|
2018-06-25 23:47:40 +00:00
|
|
|
} else {
|
|
|
|
var linem = insnLineMatch.exec(line);
|
2019-02-21 00:38:30 +00:00
|
|
|
if (linem) linenum++;
|
2018-06-25 23:47:40 +00:00
|
|
|
if (linem && linem[1]) {
|
|
|
|
var offset = parseInt(linem[1], 16);
|
|
|
|
var insns = linem[4].trim();
|
|
|
|
if (insns.length) {
|
|
|
|
lines.push({
|
|
|
|
line:linenum,
|
|
|
|
offset:offset + segofs,
|
|
|
|
insns:insns
|
|
|
|
});
|
2019-02-21 00:38:30 +00:00
|
|
|
// take back one to honor the long .byte line
|
|
|
|
if (linem[5].length == 0) linenum--;
|
2019-05-04 02:08:43 +00:00
|
|
|
} else {
|
|
|
|
var sym = linem[5];
|
|
|
|
if (sym && sym.endsWith(':')) {
|
|
|
|
sym = sym.substring(0, sym.length-1);
|
|
|
|
var symofs = symbols[sym];
|
|
|
|
if (typeof symofs === 'number') {
|
|
|
|
offset = parseInt(linem[1], 16);
|
|
|
|
segofs = symofs - offset;
|
2019-12-28 19:49:09 +00:00
|
|
|
//console.log(sym, symofs, '-', offset);
|
2019-05-04 02:08:43 +00:00
|
|
|
}
|
|
|
|
}
|
2018-06-25 23:47:40 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-06 00:14:12 +00:00
|
|
|
}
|
|
|
|
}
|
2018-06-25 23:47:40 +00:00
|
|
|
return lines;
|
2017-01-06 00:14:12 +00:00
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function assembleCA65(step:BuildStep) {
|
2017-11-07 21:41:07 +00:00
|
|
|
loadNative("ca65");
|
2018-06-19 22:42:02 +00:00
|
|
|
var errors = [];
|
2018-06-25 14:43:15 +00:00
|
|
|
gatherFiles(step, {mainFilePath:"main.s"});
|
|
|
|
var objpath = step.prefix+".o";
|
|
|
|
var lstpath = step.prefix+".lst";
|
|
|
|
if (staleFiles(step, [objpath, lstpath])) {
|
|
|
|
var objout, lstout;
|
2018-10-05 13:47:15 +00:00
|
|
|
var CA65 = emglobal.ca65({
|
2018-07-10 04:44:17 +00:00
|
|
|
instantiateWasm: moduleInstFn('ca65'),
|
2017-01-06 00:14:12 +00:00
|
|
|
noInitialRun:true,
|
2017-01-10 08:18:54 +00:00
|
|
|
//logReadFiles:true,
|
2017-01-06 00:14:12 +00:00
|
|
|
print:print_fn,
|
2018-06-19 22:42:02 +00:00
|
|
|
printErr:msvcErrorMatcher(errors),
|
2017-01-06 00:14:12 +00:00
|
|
|
});
|
|
|
|
var FS = CA65['FS'];
|
2019-04-03 21:00:05 +00:00
|
|
|
setupFS(FS, '65-'+getRootBasePlatform(step.platform));
|
2018-06-25 14:43:15 +00:00
|
|
|
populateFiles(step, FS);
|
2019-05-22 01:39:37 +00:00
|
|
|
fixParamsWithDefines(step.path, step.params);
|
2018-06-28 00:26:22 +00:00
|
|
|
execMain(step, CA65, ['-v', '-g', '-I', '/share/asminc', '-o', objpath, '-l', lstpath, step.path]);
|
2018-06-19 22:42:02 +00:00
|
|
|
if (errors.length)
|
|
|
|
return {errors:errors};
|
2018-06-25 14:43:15 +00:00
|
|
|
objout = FS.readFile(objpath, {encoding:'binary'});
|
|
|
|
lstout = FS.readFile(lstpath, {encoding:'utf8'});
|
|
|
|
putWorkFile(objpath, objout);
|
|
|
|
putWorkFile(lstpath, lstout);
|
2017-01-06 00:14:12 +00:00
|
|
|
}
|
2018-06-25 14:43:15 +00:00
|
|
|
return {
|
|
|
|
linktool:"ld65",
|
|
|
|
files:[objpath, lstpath],
|
|
|
|
args:[objpath]
|
|
|
|
};
|
2017-01-06 00:14:12 +00:00
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function linkLD65(step:BuildStep) {
|
2018-06-25 04:52:40 +00:00
|
|
|
loadNative("ld65");
|
|
|
|
var params = step.params;
|
2018-06-25 14:43:15 +00:00
|
|
|
gatherFiles(step);
|
|
|
|
var binpath = "main";
|
|
|
|
if (staleFiles(step, [binpath])) {
|
|
|
|
var errors = [];
|
2018-10-05 13:47:15 +00:00
|
|
|
var LD65 = emglobal.ld65({
|
2018-07-10 04:44:17 +00:00
|
|
|
instantiateWasm: moduleInstFn('ld65'),
|
2017-01-06 00:14:12 +00:00
|
|
|
noInitialRun:true,
|
2017-01-10 08:18:54 +00:00
|
|
|
//logReadFiles:true,
|
2017-01-06 00:14:12 +00:00
|
|
|
print:print_fn,
|
2019-02-24 15:36:38 +00:00
|
|
|
printErr:function(s) { errors.push({msg:s,line:0}); }
|
2017-01-06 00:14:12 +00:00
|
|
|
});
|
|
|
|
var FS = LD65['FS'];
|
2019-04-24 20:56:53 +00:00
|
|
|
setupFS(FS, '65-'+getRootBasePlatform(step.platform));
|
2018-06-25 14:43:15 +00:00
|
|
|
populateFiles(step, FS);
|
2018-11-28 15:31:07 +00:00
|
|
|
populateExtraFiles(step, FS, params.extra_link_files);
|
2019-05-22 01:39:37 +00:00
|
|
|
// populate .cfg file, if it is a custom one
|
|
|
|
if (workfs[params.cfgfile]) {
|
|
|
|
populateEntry(FS, params.cfgfile, workfs[params.cfgfile], null);
|
|
|
|
}
|
2019-12-07 23:20:01 +00:00
|
|
|
var libargs = params.libargs || [];
|
2019-03-13 18:52:30 +00:00
|
|
|
var cfgfile = params.cfgfile;
|
2018-06-25 14:43:15 +00:00
|
|
|
var args = ['--cfg-path', '/share/cfg',
|
2018-06-20 04:13:19 +00:00
|
|
|
'--lib-path', '/share/lib',
|
2019-03-13 18:52:30 +00:00
|
|
|
'-C', cfgfile,
|
2018-06-21 18:15:05 +00:00
|
|
|
'-Ln', 'main.vice',
|
2019-05-04 00:13:08 +00:00
|
|
|
//'--dbgfile', 'main.dbg', // TODO: get proper line numbers
|
2018-06-25 14:43:15 +00:00
|
|
|
'-o', 'main', '-m', 'main.map'].concat(step.args, libargs);
|
2018-06-28 00:26:22 +00:00
|
|
|
//console.log(args);
|
2018-06-25 14:43:15 +00:00
|
|
|
execMain(step, LD65, args);
|
|
|
|
if (errors.length)
|
2018-06-19 22:42:02 +00:00
|
|
|
return {errors:errors};
|
2018-06-25 14:43:15 +00:00
|
|
|
var aout = FS.readFile("main", {encoding:'binary'});
|
|
|
|
var mapout = FS.readFile("main.map", {encoding:'utf8'});
|
|
|
|
var viceout = FS.readFile("main.vice", {encoding:'utf8'});
|
2018-08-16 23:19:20 +00:00
|
|
|
//var dbgout = FS.readFile("main.dbg", {encoding:'utf8'});
|
2018-06-28 03:26:23 +00:00
|
|
|
putWorkFile("main", aout);
|
|
|
|
putWorkFile("main.map", mapout);
|
|
|
|
putWorkFile("main.vice", viceout);
|
|
|
|
// return unchanged if no files changed
|
2018-06-28 04:57:06 +00:00
|
|
|
if (!anyTargetChanged(step, ["main", "main.map", "main.vice"]))
|
2018-06-28 03:26:23 +00:00
|
|
|
return;
|
2018-06-21 18:15:05 +00:00
|
|
|
// parse symbol map (TODO: omit segments, constants)
|
|
|
|
var symbolmap = {};
|
|
|
|
for (var s of viceout.split("\n")) {
|
|
|
|
var toks = s.split(" ");
|
|
|
|
if (toks[0] == 'al') {
|
2019-02-21 19:19:29 +00:00
|
|
|
let ident = toks[2].substr(1);
|
2019-03-03 16:32:25 +00:00
|
|
|
if (ident.length != 5 || !ident.startsWith('L')) { // no line numbers
|
|
|
|
let ofs = parseInt(toks[1], 16);
|
|
|
|
symbolmap[ident] = ofs;
|
|
|
|
}
|
2018-06-21 18:15:05 +00:00
|
|
|
}
|
|
|
|
}
|
2019-02-21 19:19:29 +00:00
|
|
|
// build segment map
|
2019-02-21 22:41:59 +00:00
|
|
|
var seg_re = /^__(\w+)_SIZE__$/;
|
2019-08-27 20:10:10 +00:00
|
|
|
// TODO: move to Platform class
|
2019-08-27 16:12:56 +00:00
|
|
|
var segments = [];
|
2019-02-21 22:41:59 +00:00
|
|
|
segments.push({name:'CPU Stack',start:0x100,size:0x100,type:'ram'});
|
2019-03-13 18:52:30 +00:00
|
|
|
segments.push({name:'CPU Vectors',start:0xfffa,size:0x6,type:'rom'});
|
2019-02-21 22:41:59 +00:00
|
|
|
// TODO: CHR, banks, etc
|
2019-02-21 19:19:29 +00:00
|
|
|
for (let ident in symbolmap) {
|
|
|
|
let m = seg_re.exec(ident);
|
|
|
|
if (m) {
|
|
|
|
let seg = m[1];
|
|
|
|
let segstart = symbolmap['__'+seg+'_RUN__'] || symbolmap['__'+seg+'_START__'];
|
|
|
|
let segsize = symbolmap['__'+seg+'_SIZE__'];
|
|
|
|
let seglast = symbolmap['__'+seg+'_LAST__'];
|
2019-03-13 18:52:30 +00:00
|
|
|
if (segstart >= 0 && segsize > 0 && !seg.startsWith('PRG') && seg != 'RAM') { // TODO
|
2019-02-21 21:47:25 +00:00
|
|
|
var type = null;
|
2019-04-24 20:56:53 +00:00
|
|
|
if (seg.startsWith('CODE') || seg == 'STARTUP' || seg == 'RODATA' || seg.endsWith('ROM')) type = 'rom';
|
|
|
|
else if (seg == 'ZP' || seg == 'DATA' || seg == 'BSS' || seg.endsWith('RAM')) type = 'ram';
|
2019-02-21 21:47:25 +00:00
|
|
|
segments.push({name:seg, start:segstart, size:segsize, last:seglast, type:type});
|
2019-02-21 19:19:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// build listings
|
2019-12-27 22:31:24 +00:00
|
|
|
var listings : CodeListingMap = {};
|
2018-06-28 00:26:22 +00:00
|
|
|
for (var fn of step.files) {
|
|
|
|
if (fn.endsWith('.lst')) {
|
|
|
|
var lstout = FS.readFile(fn, {encoding:'utf8'});
|
2020-07-06 04:50:11 +00:00
|
|
|
lstout = lstout.split('\n\n')[1] || lstout; // remove header
|
2018-08-06 20:23:19 +00:00
|
|
|
var asmlines = parseCA65Listing(lstout, symbolmap, params, false);
|
|
|
|
var srclines = parseCA65Listing(lstout, symbolmap, params, true);
|
2018-06-28 00:26:22 +00:00
|
|
|
putWorkFile(fn, lstout);
|
2019-12-28 19:49:09 +00:00
|
|
|
// TODO: you have to get rid of all source lines to get asm listing
|
2018-06-28 00:26:22 +00:00
|
|
|
listings[fn] = {
|
|
|
|
asmlines:srclines.length ? asmlines : null,
|
|
|
|
lines:srclines.length ? srclines : asmlines,
|
|
|
|
text:lstout
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2017-01-10 08:18:54 +00:00
|
|
|
return {
|
2018-06-25 23:47:40 +00:00
|
|
|
output:aout, //.slice(0),
|
2018-06-28 00:26:22 +00:00
|
|
|
listings:listings,
|
2018-06-25 23:47:40 +00:00
|
|
|
errors:errors,
|
2018-06-21 18:15:05 +00:00
|
|
|
symbolmap:symbolmap,
|
2019-02-21 19:19:29 +00:00
|
|
|
segments:segments
|
2017-01-10 08:18:54 +00:00
|
|
|
};
|
2017-01-06 00:14:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-13 18:52:30 +00:00
|
|
|
function fixParamsWithDefines(path:string, params){
|
|
|
|
var libargs = params.libargs;
|
2019-02-15 18:54:15 +00:00
|
|
|
if (path && libargs) {
|
|
|
|
var code = getWorkFileAsString(path);
|
|
|
|
if (code) {
|
2019-05-22 01:39:37 +00:00
|
|
|
var oldcfgfile = params.cfgfile;
|
2019-02-15 18:54:15 +00:00
|
|
|
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
|
2019-05-22 01:39:37 +00:00
|
|
|
var re = /^[;]?#define\s+(\w+)\s+(\S+)/gmi; // TODO: empty string?
|
2019-02-15 18:54:15 +00:00
|
|
|
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;
|
2019-03-13 18:52:30 +00:00
|
|
|
console.log('Using libargs', index, libargs[index]);
|
|
|
|
// TODO: MMC3 mapper switch
|
|
|
|
if (ident == 'NES_MAPPER' && value == '4') {
|
|
|
|
params.cfgfile = 'nesbanked.cfg';
|
2019-05-22 01:39:37 +00:00
|
|
|
console.log("using config file", params.cfgfile);
|
2019-03-13 18:52:30 +00:00
|
|
|
}
|
2019-05-22 01:39:37 +00:00
|
|
|
} 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);
|
2019-02-15 18:54:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function compileCC65(step:BuildStep) {
|
2019-03-14 15:20:50 +00:00
|
|
|
loadNative("cc65");
|
2018-06-25 04:52:40 +00:00
|
|
|
var params = step.params;
|
2017-01-10 08:18:54 +00:00
|
|
|
// stderr
|
2019-03-23 00:26:47 +00:00
|
|
|
var re_err1 = /(.*?)[(](\d+)[)].*?: (.+)/;
|
|
|
|
var errors : WorkerError[] = [];
|
2017-01-10 08:18:54 +00:00
|
|
|
var errline = 0;
|
|
|
|
function match_fn(s) {
|
2017-02-01 18:21:17 +00:00
|
|
|
console.log(s);
|
2017-01-10 08:18:54 +00:00
|
|
|
var matches = re_err1.exec(s);
|
|
|
|
if (matches) {
|
2019-03-23 00:26:47 +00:00
|
|
|
errline = parseInt(matches[2]);
|
2017-01-10 08:18:54 +00:00
|
|
|
errors.push({
|
|
|
|
line:errline,
|
2019-03-23 00:26:47 +00:00
|
|
|
msg:matches[3],
|
|
|
|
path:matches[1]
|
2017-01-10 08:18:54 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2018-06-25 14:43:15 +00:00
|
|
|
gatherFiles(step, {mainFilePath:"main.c"});
|
|
|
|
var destpath = step.prefix + '.s';
|
|
|
|
if (staleFiles(step, [destpath])) {
|
2018-10-05 13:47:15 +00:00
|
|
|
var CC65 = emglobal.cc65({
|
2019-03-14 15:20:50 +00:00
|
|
|
instantiateWasm: moduleInstFn('cc65'),
|
2018-06-25 14:43:15 +00:00
|
|
|
noInitialRun:true,
|
|
|
|
//logReadFiles:true,
|
|
|
|
print:print_fn,
|
|
|
|
printErr:match_fn,
|
|
|
|
});
|
|
|
|
var FS = CC65['FS'];
|
2019-04-03 21:00:05 +00:00
|
|
|
setupFS(FS, '65-'+getRootBasePlatform(step.platform));
|
2018-06-25 14:43:15 +00:00
|
|
|
populateFiles(step, FS);
|
2019-03-13 18:52:30 +00:00
|
|
|
fixParamsWithDefines(step.path, params);
|
2019-12-19 00:47:40 +00:00
|
|
|
var args = ['-T', '-g',
|
2019-03-14 15:20:50 +00:00
|
|
|
'-Oirs', // don't inline CodeSizeFactor 200? (no -Oi)
|
2018-08-15 04:43:52 +00:00
|
|
|
'-Cl', // static locals
|
2018-06-25 14:43:15 +00:00
|
|
|
'-I', '/share/include',
|
2019-05-04 00:13:08 +00:00
|
|
|
'-I', '.',
|
2019-12-19 00:47:40 +00:00
|
|
|
];
|
2020-02-04 17:58:46 +00:00
|
|
|
if (params.define) {
|
|
|
|
params.define.forEach((x) => args.push('-D'+x));
|
|
|
|
}
|
|
|
|
if (step.mainfile) {
|
|
|
|
args.unshift.apply(args, ["-D", "__MAIN__"]);
|
|
|
|
}
|
2019-12-19 00:47:40 +00:00
|
|
|
args.push(step.path);
|
2019-12-24 14:29:46 +00:00
|
|
|
//console.log(args);
|
2019-12-19 00:47:40 +00:00
|
|
|
execMain(step, CC65, args);
|
2018-06-25 14:43:15 +00:00
|
|
|
if (errors.length)
|
|
|
|
return {errors:errors};
|
|
|
|
var asmout = FS.readFile(destpath, {encoding:'utf8'});
|
|
|
|
putWorkFile(destpath, asmout);
|
2017-01-10 08:18:54 +00:00
|
|
|
}
|
2018-06-25 04:52:40 +00:00
|
|
|
return {
|
|
|
|
nexttool:"ca65",
|
2018-06-25 14:43:15 +00:00
|
|
|
path:destpath,
|
2018-11-20 17:31:19 +00:00
|
|
|
args:[destpath],
|
|
|
|
files:[destpath],
|
2018-06-25 04:52:40 +00:00
|
|
|
};
|
2017-01-06 00:14:12 +00:00
|
|
|
}
|
|
|
|
|
2017-01-15 18:31:52 +00:00
|
|
|
function hexToArray(s, ofs) {
|
|
|
|
var buf = new ArrayBuffer(s.length/2);
|
|
|
|
var arr = new Uint8Array(buf);
|
|
|
|
for (var i=0; i<arr.length; i++) {
|
|
|
|
arr[i] = parseInt(s.slice(i*2+ofs,i*2+ofs+2), 16);
|
|
|
|
}
|
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
|
2019-06-02 14:53:39 +00:00
|
|
|
function parseIHX(ihx, rom_start, rom_size, errors) {
|
2017-05-02 13:09:53 +00:00
|
|
|
var output = new Uint8Array(new ArrayBuffer(rom_size));
|
2019-06-02 14:53:39 +00:00
|
|
|
var high_size = 0;
|
2017-01-15 18:31:52 +00:00
|
|
|
for (var s of ihx.split("\n")) {
|
|
|
|
if (s[0] == ':') {
|
|
|
|
var arr = hexToArray(s, 1);
|
|
|
|
var count = arr[0];
|
2017-05-02 13:09:53 +00:00
|
|
|
var address = (arr[1]<<8) + arr[2] - rom_start;
|
2017-01-15 18:31:52 +00:00
|
|
|
var rectype = arr[3];
|
2019-06-02 14:53:39 +00:00
|
|
|
//console.log(rectype,address.toString(16),count,arr);
|
2017-01-15 18:31:52 +00:00
|
|
|
if (rectype == 0) {
|
|
|
|
for (var i=0; i<count; i++) {
|
|
|
|
var b = arr[4+i];
|
|
|
|
output[i+address] = b;
|
|
|
|
}
|
2019-06-02 14:53:39 +00:00
|
|
|
if (i+address > high_size) high_size = i+address;
|
2017-01-15 18:31:52 +00:00
|
|
|
} else if (rectype == 1) {
|
2019-06-02 14:53:39 +00:00
|
|
|
break;
|
2018-11-28 15:31:07 +00:00
|
|
|
} else {
|
|
|
|
console.log(s); // unknown record type
|
2017-01-15 18:31:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-02 14:53:39 +00:00
|
|
|
// TODO: return ROM anyway?
|
|
|
|
if (high_size > rom_size) {
|
|
|
|
//errors.push({line:0, msg:"ROM size too large: 0x" + high_size.toString(16) + " > 0x" + rom_size.toString(16)});
|
|
|
|
}
|
2018-08-28 17:59:12 +00:00
|
|
|
return output;
|
2017-01-15 18:31:52 +00:00
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function assembleSDASZ80(step:BuildStep) {
|
2017-11-07 21:41:07 +00:00
|
|
|
loadNative("sdasz80");
|
2017-01-15 18:31:52 +00:00
|
|
|
var objout, lstout, symout;
|
2018-06-19 22:42:02 +00:00
|
|
|
var errors = [];
|
2018-06-25 14:43:15 +00:00
|
|
|
gatherFiles(step, {mainFilePath:"main.asm"});
|
|
|
|
var objpath = step.prefix + ".rel";
|
|
|
|
var lstpath = step.prefix + ".lst";
|
|
|
|
if (staleFiles(step, [objpath, lstpath])) {
|
2017-01-22 14:35:04 +00:00
|
|
|
//?ASxxxx-Error-<o> in line 1 of main.asm null
|
|
|
|
// <o> .org in REL area or directive / mnemonic error
|
2018-09-21 12:39:15 +00:00
|
|
|
// ?ASxxxx-Error-<q> in line 1627 of cosmic.asm
|
|
|
|
// <q> missing or improper operators, terminators, or delimiters
|
|
|
|
var match_asm_re1 = / in line (\d+) of (\S+)/; // TODO
|
|
|
|
var match_asm_re2 = / <\w> (.+)/; // TODO
|
|
|
|
var errline = 0;
|
|
|
|
var errpath = step.path;
|
2018-10-05 13:47:15 +00:00
|
|
|
var match_asm_fn = (s:string) => {
|
2018-09-21 12:39:15 +00:00
|
|
|
var m = match_asm_re1.exec(s);
|
|
|
|
if (m) {
|
|
|
|
errline = parseInt(m[1]);
|
|
|
|
errpath = m[2];
|
2018-08-29 01:41:51 +00:00
|
|
|
} else {
|
2018-09-21 12:39:15 +00:00
|
|
|
m = match_asm_re2.exec(s);
|
|
|
|
if (m) {
|
|
|
|
errors.push({
|
|
|
|
line:errline,
|
|
|
|
path:errpath,
|
|
|
|
msg:m[1]
|
|
|
|
});
|
|
|
|
}
|
2017-01-15 18:31:52 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-05 13:47:15 +00:00
|
|
|
var ASZ80 = emglobal.sdasz80({
|
2018-07-10 04:44:17 +00:00
|
|
|
instantiateWasm: moduleInstFn('sdasz80'),
|
2017-01-15 18:31:52 +00:00
|
|
|
noInitialRun:true,
|
|
|
|
//logReadFiles:true,
|
2017-01-22 14:35:04 +00:00
|
|
|
print:match_asm_fn,
|
|
|
|
printErr:match_asm_fn,
|
2017-01-15 18:31:52 +00:00
|
|
|
});
|
|
|
|
var FS = ASZ80['FS'];
|
2018-06-25 14:43:15 +00:00
|
|
|
populateFiles(step, FS);
|
2018-06-25 04:52:40 +00:00
|
|
|
execMain(step, ASZ80, ['-plosgffwy', step.path]);
|
2018-06-19 22:42:02 +00:00
|
|
|
if (errors.length) {
|
|
|
|
return {errors:errors};
|
2017-01-15 18:31:52 +00:00
|
|
|
}
|
2018-06-25 14:43:15 +00:00
|
|
|
objout = FS.readFile(objpath, {encoding:'utf8'});
|
|
|
|
lstout = FS.readFile(lstpath, {encoding:'utf8'});
|
|
|
|
putWorkFile(objpath, objout);
|
|
|
|
putWorkFile(lstpath, lstout);
|
2018-06-25 04:52:40 +00:00
|
|
|
}
|
2018-06-25 14:43:15 +00:00
|
|
|
return {
|
|
|
|
linktool:"sdldz80",
|
|
|
|
files:[objpath, lstpath],
|
|
|
|
args:[objpath]
|
|
|
|
};
|
|
|
|
//symout = FS.readFile("main.sym", {encoding:'utf8'});
|
2018-06-25 04:52:40 +00:00
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function linkSDLDZ80(step:BuildStep)
|
2018-06-25 04:52:40 +00:00
|
|
|
{
|
|
|
|
loadNative("sdldz80");
|
|
|
|
var errors = [];
|
2018-06-25 14:43:15 +00:00
|
|
|
gatherFiles(step);
|
|
|
|
var binpath = "main.ihx";
|
|
|
|
if (staleFiles(step, [binpath])) {
|
2017-01-22 14:35:04 +00:00
|
|
|
//?ASlink-Warning-Undefined Global '__divsint' referenced by module 'main'
|
|
|
|
var match_aslink_re = /\?ASlink-(\w+)-(.+)/;
|
2018-10-05 13:47:15 +00:00
|
|
|
var match_aslink_fn = (s:string) => {
|
2017-01-22 14:35:04 +00:00
|
|
|
var matches = match_aslink_re.exec(s);
|
|
|
|
if (matches) {
|
2018-06-19 22:42:02 +00:00
|
|
|
errors.push({
|
2018-07-03 03:45:08 +00:00
|
|
|
line:0,
|
2017-01-22 14:35:04 +00:00
|
|
|
msg:matches[2]
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2018-06-25 04:52:40 +00:00
|
|
|
var params = step.params;
|
2018-10-05 13:47:15 +00:00
|
|
|
var LDZ80 = emglobal.sdldz80({
|
2018-07-10 04:44:17 +00:00
|
|
|
instantiateWasm: moduleInstFn('sdldz80'),
|
2017-01-15 18:31:52 +00:00
|
|
|
noInitialRun:true,
|
|
|
|
//logReadFiles:true,
|
2017-01-22 14:35:04 +00:00
|
|
|
print:match_aslink_fn,
|
|
|
|
printErr:match_aslink_fn,
|
2017-01-15 18:31:52 +00:00
|
|
|
});
|
|
|
|
var FS = LDZ80['FS'];
|
2017-01-23 21:21:45 +00:00
|
|
|
setupFS(FS, 'sdcc');
|
2018-06-25 04:52:40 +00:00
|
|
|
populateFiles(step, FS);
|
2018-11-28 15:31:07 +00:00
|
|
|
populateExtraFiles(step, FS, params.extra_link_files);
|
2018-06-25 06:12:11 +00:00
|
|
|
// TODO: coleco hack so that -u flag works
|
2018-11-19 14:10:13 +00:00
|
|
|
if (step.platform.startsWith("coleco")) {
|
2018-06-25 06:12:11 +00:00
|
|
|
FS.writeFile('crt0.rel', FS.readFile('/share/lib/coleco/crt0.rel', {encoding:'utf8'}));
|
|
|
|
FS.writeFile('crt0.lst', '\n'); // TODO: needed so -u flag works
|
2017-05-02 13:09:53 +00:00
|
|
|
}
|
2018-06-28 00:26:22 +00:00
|
|
|
var args = ['-mjwxyu',
|
2018-06-25 04:52:40 +00:00
|
|
|
'-i', 'main.ihx', // TODO: main?
|
2017-01-15 18:31:52 +00:00
|
|
|
'-b', '_CODE=0x'+params.code_start.toString(16),
|
|
|
|
'-b', '_DATA=0x'+params.data_start.toString(16),
|
2017-01-23 21:21:45 +00:00
|
|
|
'-k', '/share/lib/z80',
|
2017-05-02 13:09:53 +00:00
|
|
|
'-l', 'z80'];
|
2018-06-25 06:12:11 +00:00
|
|
|
if (params.extra_link_args)
|
2017-05-02 13:09:53 +00:00
|
|
|
args.push.apply(args, params.extra_link_args);
|
2018-06-25 06:12:11 +00:00
|
|
|
args.push.apply(args, step.args);
|
2018-11-28 15:31:07 +00:00
|
|
|
//console.log(args);
|
2018-06-25 04:52:40 +00:00
|
|
|
execMain(step, LDZ80, args);
|
2017-01-15 18:31:52 +00:00
|
|
|
var hexout = FS.readFile("main.ihx", {encoding:'utf8'});
|
2019-02-21 22:41:59 +00:00
|
|
|
var noiout = FS.readFile("main.noi", {encoding:'utf8'});
|
2018-06-28 03:26:23 +00:00
|
|
|
putWorkFile("main.ihx", hexout);
|
2019-02-21 22:41:59 +00:00
|
|
|
putWorkFile("main.noi", noiout);
|
2018-06-28 03:26:23 +00:00
|
|
|
// return unchanged if no files changed
|
2018-06-28 04:57:06 +00:00
|
|
|
if (!anyTargetChanged(step, ["main.ihx", "main.noi"]))
|
2018-06-28 03:26:23 +00:00
|
|
|
return;
|
2019-08-10 15:11:22 +00:00
|
|
|
// parse binary file
|
2019-06-02 14:53:39 +00:00
|
|
|
var binout = parseIHX(hexout, params.rom_start!==undefined?params.rom_start:params.code_start, params.rom_size, errors);
|
|
|
|
if (errors.length) {
|
|
|
|
return {errors:errors};
|
|
|
|
}
|
2019-08-10 15:11:22 +00:00
|
|
|
// parse listings
|
2019-12-27 22:31:24 +00:00
|
|
|
var listings : CodeListingMap = {};
|
2018-06-28 00:26:22 +00:00
|
|
|
for (var fn of step.files) {
|
|
|
|
if (fn.endsWith('.lst')) {
|
|
|
|
var rstout = FS.readFile(fn.replace('.lst','.rst'), {encoding:'utf8'});
|
|
|
|
// 0000 21 02 00 [10] 52 ld hl, #2
|
2019-06-11 13:39:45 +00:00
|
|
|
var asmlines = parseListing(rstout, /^\s*([0-9A-F]{4})\s+([0-9A-F][0-9A-F r]*[0-9A-F])\s+\[([0-9 ]+)\]?\s+(\d+) (.*)/i, 4, 1, 2, 3);
|
2018-06-28 00:26:22 +00:00
|
|
|
var srclines = parseSourceLines(rstout, /^\s+\d+ ;<stdin>:(\d+):/i, /^\s*([0-9A-F]{4})/i);
|
|
|
|
putWorkFile(fn, rstout);
|
2018-08-27 21:31:49 +00:00
|
|
|
// TODO: you have to get rid of all source lines to get asm listing
|
2018-06-28 00:26:22 +00:00
|
|
|
listings[fn] = {
|
|
|
|
asmlines:srclines.length ? asmlines : null,
|
|
|
|
lines:srclines.length ? srclines : asmlines,
|
|
|
|
text:rstout
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2017-01-16 04:47:12 +00:00
|
|
|
// parse symbol map
|
|
|
|
var symbolmap = {};
|
2019-02-21 22:41:59 +00:00
|
|
|
for (var s of noiout.split("\n")) {
|
2017-01-16 04:47:12 +00:00
|
|
|
var toks = s.split(" ");
|
2019-03-03 16:32:25 +00:00
|
|
|
if (toks[0] == 'DEF' && !toks[1].startsWith("A$")) {
|
2017-04-22 01:56:49 +00:00
|
|
|
symbolmap[toks[1]] = parseInt(toks[2], 16);
|
2017-01-16 04:47:12 +00:00
|
|
|
}
|
|
|
|
}
|
2019-02-21 22:41:59 +00:00
|
|
|
// build segment map
|
|
|
|
var seg_re = /^s__(\w+)$/;
|
2019-08-27 16:12:56 +00:00
|
|
|
var segments = [];
|
2019-02-21 22:41:59 +00:00
|
|
|
// TODO: use stack params for stack segment
|
|
|
|
for (let ident in symbolmap) {
|
|
|
|
let m = seg_re.exec(ident);
|
|
|
|
if (m) {
|
|
|
|
let seg = m[1];
|
|
|
|
let segstart = symbolmap[ident]; // s__SEG
|
|
|
|
let segsize = symbolmap['l__'+seg]; // l__SEG
|
|
|
|
if (segstart >= 0 && segsize > 0) {
|
|
|
|
var type = null;
|
2019-06-02 14:53:39 +00:00
|
|
|
if (['INITIALIZER','GSINIT','GSFINAL'].includes(seg)) type = 'rom';
|
|
|
|
else if (seg.startsWith('CODE')) type = 'rom';
|
2019-02-21 22:41:59 +00:00
|
|
|
else if (['DATA','INITIALIZED'].includes(seg)) type = 'ram';
|
|
|
|
if (type == 'rom' || segstart > 0) // ignore HEADER0, CABS0, etc (TODO?)
|
|
|
|
segments.push({name:seg, start:segstart, size:segsize, type:type});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-01-15 18:31:52 +00:00
|
|
|
return {
|
2019-06-02 14:53:39 +00:00
|
|
|
output:binout,
|
2018-06-28 00:26:22 +00:00
|
|
|
listings:listings,
|
|
|
|
errors:errors,
|
2017-04-22 01:56:49 +00:00
|
|
|
symbolmap:symbolmap,
|
2019-02-21 22:41:59 +00:00
|
|
|
segments:segments
|
2017-01-15 18:31:52 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function compileSDCC(step:BuildStep) {
|
2018-06-26 06:56:36 +00:00
|
|
|
|
2018-06-25 14:43:15 +00:00
|
|
|
gatherFiles(step, {
|
2018-06-25 04:52:40 +00:00
|
|
|
mainFilePath:"main.c" // not used
|
2017-01-13 02:21:35 +00:00
|
|
|
});
|
2018-06-25 14:43:15 +00:00
|
|
|
var outpath = step.prefix + ".asm";
|
|
|
|
if (staleFiles(step, [outpath])) {
|
|
|
|
var errors = [];
|
|
|
|
var params = step.params;
|
|
|
|
loadNative('sdcc');
|
2019-09-29 18:41:29 +00:00
|
|
|
var SDCC = emglobal.sdcc({
|
2018-07-10 04:44:17 +00:00
|
|
|
instantiateWasm: moduleInstFn('sdcc'),
|
2018-06-25 14:43:15 +00:00
|
|
|
noInitialRun:true,
|
|
|
|
noFSInit:true,
|
|
|
|
print:print_fn,
|
|
|
|
printErr:msvcErrorMatcher(errors),
|
2018-07-10 04:44:17 +00:00
|
|
|
//TOTAL_MEMORY:256*1024*1024,
|
2018-06-25 14:43:15 +00:00
|
|
|
});
|
|
|
|
var FS = SDCC['FS'];
|
|
|
|
populateFiles(step, FS);
|
|
|
|
// load source file and preprocess
|
2018-11-28 16:10:24 +00:00
|
|
|
var code = getWorkFileAsString(step.path);
|
2019-12-11 03:19:12 +00:00
|
|
|
var preproc = preprocessMCPP(step, 'sdcc');
|
2018-06-25 14:43:15 +00:00
|
|
|
if (preproc.errors) return preproc;
|
|
|
|
else code = preproc.code;
|
|
|
|
// pipe file to stdin
|
|
|
|
setupStdin(FS, code);
|
|
|
|
setupFS(FS, 'sdcc');
|
|
|
|
var args = ['--vc', '--std-sdcc99', '-mz80', //'-Wall',
|
2018-11-26 15:03:35 +00:00
|
|
|
'--c1mode',
|
|
|
|
//'--debug',
|
2018-06-25 14:43:15 +00:00
|
|
|
//'-S', 'main.c',
|
|
|
|
//'--asm=sdasz80',
|
|
|
|
//'--reserve-regs-iy',
|
|
|
|
'--less-pedantic',
|
|
|
|
///'--fomit-frame-pointer',
|
2018-12-06 14:41:45 +00:00
|
|
|
//'--opt-code-speed',
|
|
|
|
//'--max-allocs-per-node', '1000',
|
2018-06-25 14:43:15 +00:00
|
|
|
//'--cyclomatic',
|
2018-11-26 15:03:35 +00:00
|
|
|
//'--nooverlay',
|
|
|
|
//'--nogcse',
|
|
|
|
//'--nolabelopt',
|
|
|
|
//'--noinvariant',
|
|
|
|
//'--noinduction',
|
|
|
|
//'--nojtbound',
|
|
|
|
//'--noloopreverse',
|
2018-06-25 14:43:15 +00:00
|
|
|
'-o', outpath];
|
2019-06-01 13:50:31 +00:00
|
|
|
// if "#pragma opt_code" found do not disable optimziations
|
|
|
|
if (!/^\s*#pragma\s+opt_code/m.exec(code)) {
|
|
|
|
args.push.apply(args, [
|
|
|
|
'--oldralloc',
|
|
|
|
'--no-peep',
|
|
|
|
'--nolospre'
|
|
|
|
]);
|
|
|
|
}
|
2018-06-25 14:43:15 +00:00
|
|
|
if (params.extra_compile_args) {
|
|
|
|
args.push.apply(args, params.extra_compile_args);
|
|
|
|
}
|
|
|
|
execMain(step, SDCC, args);
|
|
|
|
// TODO: preprocessor errors w/ correct file
|
|
|
|
if (errors.length /* && nwarnings < msvc_errors.length*/) {
|
|
|
|
return {errors:errors};
|
|
|
|
}
|
|
|
|
// massage the asm output
|
|
|
|
var asmout = FS.readFile(outpath, {encoding:'utf8'});
|
2017-02-18 22:50:51 +00:00
|
|
|
asmout = " .area _HOME\n .area _CODE\n .area _INITIALIZER\n .area _DATA\n .area _INITIALIZED\n .area _BSEG\n .area _BSS\n .area _HEAP\n" + asmout;
|
2018-06-25 14:43:15 +00:00
|
|
|
putWorkFile(outpath, asmout);
|
2017-02-01 18:21:17 +00:00
|
|
|
}
|
2018-06-25 04:52:40 +00:00
|
|
|
return {
|
|
|
|
nexttool:"sdasz80",
|
2018-06-25 14:43:15 +00:00
|
|
|
path:outpath,
|
2018-11-20 17:31:19 +00:00
|
|
|
args:[outpath],
|
|
|
|
files:[outpath],
|
2018-06-25 04:52:40 +00:00
|
|
|
};
|
2017-01-13 02:21:35 +00:00
|
|
|
}
|
|
|
|
|
2018-11-22 17:28:50 +00:00
|
|
|
function makeCPPSafe(s:string) : string {
|
|
|
|
return s.replace(/[^A-Za-z0-9_]/g,'_');
|
|
|
|
}
|
|
|
|
|
2019-12-11 03:19:12 +00:00
|
|
|
function preprocessMCPP(step:BuildStep, filesys:string) {
|
2017-01-22 14:35:04 +00:00
|
|
|
load("mcpp");
|
2018-08-12 23:59:08 +00:00
|
|
|
var platform = step.platform;
|
2019-02-25 21:04:15 +00:00
|
|
|
var params = PLATFORM_PARAMS[getBasePlatform(platform)];
|
2017-05-02 13:09:53 +00:00
|
|
|
if (!params) throw Error("Platform not supported: " + platform);
|
2017-01-23 21:21:45 +00:00
|
|
|
// <stdin>:2: error: Can't open include file "foo.h"
|
|
|
|
var errors = [];
|
2018-08-18 00:46:55 +00:00
|
|
|
var match_fn = makeErrorMatcher(errors, /<stdin>:(\d+): (.+)/, 1, 2, step.path);
|
2018-10-05 13:47:15 +00:00
|
|
|
var MCPP = emglobal.mcpp({
|
2017-01-22 14:35:04 +00:00
|
|
|
noInitialRun:true,
|
|
|
|
noFSInit:true,
|
|
|
|
print:print_fn,
|
2017-01-23 21:21:45 +00:00
|
|
|
printErr:match_fn,
|
2017-01-22 14:35:04 +00:00
|
|
|
});
|
|
|
|
var FS = MCPP['FS'];
|
2019-12-11 03:19:12 +00:00
|
|
|
if (filesys) setupFS(FS, filesys);
|
2018-08-12 23:59:08 +00:00
|
|
|
populateFiles(step, FS);
|
2018-11-28 15:31:07 +00:00
|
|
|
populateExtraFiles(step, FS, params.extra_compile_files);
|
2018-06-25 04:52:40 +00:00
|
|
|
// TODO: make configurable by other compilers
|
2017-05-02 13:09:53 +00:00
|
|
|
var args = [
|
2017-01-23 21:21:45 +00:00
|
|
|
"-D", "__8BITWORKSHOP__",
|
2017-02-20 23:50:29 +00:00
|
|
|
"-D", "__SDCC_z80",
|
2018-11-22 17:28:50 +00:00
|
|
|
"-D", makeCPPSafe(platform.toUpperCase()),
|
2017-01-23 21:21:45 +00:00
|
|
|
"-I", "/share/include",
|
|
|
|
"-Q",
|
2018-08-12 23:59:08 +00:00
|
|
|
step.path, "main.i"];
|
2018-11-29 23:55:20 +00:00
|
|
|
if (step.mainfile) {
|
|
|
|
args.unshift.apply(args, ["-D", "__MAIN__"]);
|
|
|
|
}
|
2017-05-02 13:09:53 +00:00
|
|
|
if (params.extra_preproc_args) {
|
|
|
|
args.push.apply(args, params.extra_preproc_args);
|
|
|
|
}
|
|
|
|
MCPP.callMain(args);
|
2018-06-25 04:52:40 +00:00
|
|
|
if (errors.length)
|
|
|
|
return {errors:errors};
|
|
|
|
var iout = FS.readFile("main.i", {encoding:'utf8'});
|
|
|
|
iout = iout.replace(/^#line /gm,'\n# ');
|
2017-01-23 21:21:45 +00:00
|
|
|
try {
|
|
|
|
var errout = FS.readFile("mcpp.err", {encoding:'utf8'});
|
|
|
|
if (errout.length) {
|
|
|
|
// //main.c:2: error: Can't open include file "stdiosd.h"
|
2019-03-23 00:26:47 +00:00
|
|
|
var errors = extractErrors(/([^:]+):(\d+): (.+)/, errout.split("\n"), step.path, 2, 3, 1);
|
2017-01-29 21:06:05 +00:00
|
|
|
if (errors.length == 0) {
|
2018-07-03 03:45:08 +00:00
|
|
|
errors = [{line:0, msg:errout}];
|
2017-01-29 21:06:05 +00:00
|
|
|
}
|
|
|
|
return {errors: errors};
|
2017-01-23 21:21:45 +00:00
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
//
|
2017-01-22 14:35:04 +00:00
|
|
|
}
|
|
|
|
return {code:iout};
|
|
|
|
}
|
|
|
|
|
2018-06-28 00:26:22 +00:00
|
|
|
// TODO: must be a better way to do all this
|
2017-04-27 11:48:59 +00:00
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function detectModuleName(code:string) {
|
2018-10-03 19:06:48 +00:00
|
|
|
var m = /^\s*module\s+(\w+_top)\b/m.exec(code)
|
|
|
|
|| /^\s*module\s+(top)\b/m.exec(code)
|
|
|
|
|| /^\s*module\s+(\w+)\b/m.exec(code);
|
2017-11-14 14:33:15 +00:00
|
|
|
return m ? m[1] : null;
|
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function detectTopModuleName(code:string) {
|
2017-11-20 01:32:58 +00:00
|
|
|
var topmod = detectModuleName(code) || "top";
|
2018-10-03 19:06:48 +00:00
|
|
|
var m = /^\s*module\s+(\w+?_top)/m.exec(code);
|
2017-11-20 01:32:58 +00:00
|
|
|
if (m && m[1]) topmod = m[1];
|
|
|
|
return topmod;
|
|
|
|
}
|
|
|
|
|
2018-12-15 18:14:40 +00:00
|
|
|
// cached stuff (TODO)
|
2018-03-03 02:41:56 +00:00
|
|
|
var jsasm_module_top;
|
|
|
|
var jsasm_module_output;
|
|
|
|
var jsasm_module_key;
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function compileJSASM(asmcode:string, platform, options, is_inline) {
|
2018-07-11 04:04:28 +00:00
|
|
|
loadGen("worker/assembler");
|
2018-10-05 18:02:46 +00:00
|
|
|
var asm = new emglobal.exports.Assembler();
|
2018-03-02 05:15:33 +00:00
|
|
|
var includes = [];
|
2018-11-28 16:10:24 +00:00
|
|
|
asm.loadJSON = (filename:string) => {
|
2018-12-15 18:14:40 +00:00
|
|
|
var jsontext = getWorkFileAsString(filename);
|
2019-11-13 20:45:18 +00:00
|
|
|
if (!jsontext) throw Error("could not load " + filename);
|
2018-11-28 16:10:24 +00:00
|
|
|
return JSON.parse(jsontext);
|
2018-02-18 05:12:09 +00:00
|
|
|
};
|
2018-03-02 05:15:33 +00:00
|
|
|
asm.loadInclude = function(filename) {
|
|
|
|
if (!filename.startsWith('"') || !filename.endsWith('"'))
|
|
|
|
return 'Expected filename in "double quotes"';
|
|
|
|
filename = filename.substr(1, filename.length-2);
|
|
|
|
includes.push(filename);
|
|
|
|
};
|
2018-03-03 03:39:32 +00:00
|
|
|
var loaded_module = false;
|
2018-03-02 05:15:33 +00:00
|
|
|
asm.loadModule = function(top_module) {
|
|
|
|
// compile last file in list
|
2018-03-03 03:39:32 +00:00
|
|
|
loaded_module = true;
|
2018-03-03 02:41:56 +00:00
|
|
|
var key = top_module + '/' + includes;
|
2018-12-15 18:14:40 +00:00
|
|
|
if (jsasm_module_key != key) {
|
2018-03-03 03:39:32 +00:00
|
|
|
jsasm_module_key = key;
|
2018-12-15 18:14:40 +00:00
|
|
|
jsasm_module_output = null;
|
2018-03-03 02:41:56 +00:00
|
|
|
}
|
2018-12-15 18:14:40 +00:00
|
|
|
jsasm_module_top = top_module;
|
|
|
|
var main_filename = includes[includes.length-1];
|
|
|
|
// TODO: take out .asm dependency
|
|
|
|
var voutput = compileVerilator({platform:platform, files:includes, path:main_filename, tool:'verilator'});
|
|
|
|
if (voutput)
|
|
|
|
jsasm_module_output = voutput;
|
2018-03-02 05:15:33 +00:00
|
|
|
}
|
2018-02-18 05:12:09 +00:00
|
|
|
var result = asm.assembleFile(asmcode);
|
2018-03-03 03:39:32 +00:00
|
|
|
if (loaded_module && jsasm_module_output) {
|
2018-12-15 18:14:40 +00:00
|
|
|
// errors? return them
|
|
|
|
if (jsasm_module_output.errors && jsasm_module_output.errors.length)
|
|
|
|
return jsasm_module_output;
|
|
|
|
// return program ROM array
|
2018-03-02 05:15:33 +00:00
|
|
|
var asmout = result.output;
|
2018-06-28 00:26:22 +00:00
|
|
|
// TODO: unify
|
2018-03-03 02:41:56 +00:00
|
|
|
result.output = jsasm_module_output.output;
|
2018-03-02 05:15:33 +00:00
|
|
|
result.output.program_rom = asmout;
|
|
|
|
// cpu_platform__DOT__program_rom
|
2018-03-03 02:41:56 +00:00
|
|
|
result.output.program_rom_variable = jsasm_module_top + "__DOT__program_rom";
|
2018-07-31 18:41:27 +00:00
|
|
|
result.listings = {};
|
|
|
|
result.listings[options.path] = {lines:result.lines};
|
2018-03-02 05:15:33 +00:00
|
|
|
}
|
2018-02-18 05:12:09 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function compileJSASMStep(step:BuildStep) {
|
2018-12-15 18:14:40 +00:00
|
|
|
gatherFiles(step);
|
|
|
|
var code = getWorkFileAsString(step.path);
|
2018-06-28 00:26:22 +00:00
|
|
|
var platform = step.platform || 'verilog';
|
2018-12-15 18:14:40 +00:00
|
|
|
return compileJSASM(code, platform, step, false);
|
2018-06-28 00:26:22 +00:00
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function compileInlineASM(code:string, platform, options, errors, asmlines) {
|
2018-02-18 05:12:09 +00:00
|
|
|
code = code.replace(/__asm\b([\s\S]+?)\b__endasm\b/g, function(s,asmcode,index) {
|
|
|
|
var firstline = code.substr(0,index).match(/\n/g).length;
|
2018-03-02 05:15:33 +00:00
|
|
|
var asmout = compileJSASM(asmcode, platform, options, true);
|
2018-02-15 00:26:40 +00:00
|
|
|
if (asmout.errors && asmout.errors.length) {
|
2018-02-21 19:32:11 +00:00
|
|
|
for (var i=0; i<asmout.errors.length; i++) {
|
|
|
|
asmout.errors[i].line += firstline;
|
|
|
|
errors.push(asmout.errors[i]);
|
|
|
|
}
|
2018-02-15 00:26:40 +00:00
|
|
|
return "";
|
|
|
|
} else if (asmout.output) {
|
2018-10-05 13:47:15 +00:00
|
|
|
let s = "";
|
2018-02-15 00:26:40 +00:00
|
|
|
var out = asmout.output;
|
|
|
|
for (var i=0; i<out.length; i++) {
|
|
|
|
if (i>0) s += ",";
|
2018-02-18 05:12:09 +00:00
|
|
|
s += 0|out[i];
|
2018-02-15 00:26:40 +00:00
|
|
|
}
|
2018-02-28 03:35:42 +00:00
|
|
|
if (asmlines) {
|
2018-03-02 05:15:33 +00:00
|
|
|
var al = asmout.lines;
|
2018-02-28 03:35:42 +00:00
|
|
|
for (var i=0; i<al.length; i++) {
|
|
|
|
al[i].line += firstline;
|
|
|
|
asmlines.push(al[i]);
|
|
|
|
}
|
|
|
|
}
|
2018-02-15 00:26:40 +00:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
});
|
2018-02-21 19:32:11 +00:00
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function compileVerilator(step:BuildStep) {
|
2018-02-21 19:32:11 +00:00
|
|
|
loadNative("verilator_bin");
|
2018-07-11 04:04:28 +00:00
|
|
|
loadGen("worker/verilator2js");
|
2018-06-25 16:02:06 +00:00
|
|
|
var platform = step.platform || 'verilog';
|
2018-02-21 19:32:11 +00:00
|
|
|
var errors = [];
|
|
|
|
var asmlines = [];
|
2018-12-15 18:14:40 +00:00
|
|
|
gatherFiles(step);
|
|
|
|
// compile verilog if files are stale
|
|
|
|
var outjs = "main.js";
|
|
|
|
if (staleFiles(step, [outjs])) {
|
2019-08-29 01:02:41 +00:00
|
|
|
// TODO: %Error: Specified --top-module 'ALU' isn't at the top level, it's under another cell 'cpu'
|
2018-12-15 18:14:40 +00:00
|
|
|
var match_fn = makeErrorMatcher(errors, /%(.+?): (.+?):(\d+)?[:]?\s*(.+)/i, 3, 4, step.path, 2);
|
|
|
|
var verilator_mod = emglobal.verilator_bin({
|
|
|
|
instantiateWasm: moduleInstFn('verilator_bin'),
|
|
|
|
noInitialRun:true,
|
|
|
|
print:print_fn,
|
|
|
|
printErr:match_fn,
|
|
|
|
TOTAL_MEMORY:256*1024*1024,
|
|
|
|
});
|
|
|
|
var code = getWorkFileAsString(step.path);
|
|
|
|
var topmod = detectTopModuleName(code);
|
|
|
|
var FS = verilator_mod['FS'];
|
|
|
|
populateFiles(step, FS, {
|
|
|
|
mainFilePath:step.path,
|
|
|
|
processFn:(code) => {
|
2019-05-02 03:33:49 +00:00
|
|
|
code = compileInlineASM(code, platform, step, errors, asmlines);
|
|
|
|
return code;
|
2018-12-15 18:14:40 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
starttime();
|
|
|
|
try {
|
2020-07-27 13:04:48 +00:00
|
|
|
var args = ["--cc", "-O3"/*abcdefstzsuka*/, "-DEXT_INLINE_ASM", "-DTOPMOD__"+topmod,
|
2018-12-15 18:14:40 +00:00
|
|
|
"-Wall", "-Wno-DECLFILENAME", "-Wno-UNUSED", '--report-unoptflat',
|
|
|
|
"--x-assign", "fast", "--noassert", "--pins-bv", "33",
|
|
|
|
"--top-module", topmod, step.path]
|
|
|
|
verilator_mod.callMain(args);
|
|
|
|
} catch (e) {
|
|
|
|
console.log(e);
|
|
|
|
errors.push({line:0,msg:"Compiler internal error: " + e});
|
|
|
|
}
|
|
|
|
endtime("compile");
|
|
|
|
// remove boring errors
|
|
|
|
errors = errors.filter(function(e) { return !/Exiting due to \d+/.exec(e.msg); }, errors);
|
|
|
|
errors = errors.filter(function(e) { return !/Use ["][/][*]/.exec(e.msg); }, errors);
|
|
|
|
if (errors.length) {
|
|
|
|
return {errors:errors};
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
var h_file = FS.readFile("obj_dir/V"+topmod+".h", {encoding:'utf8'});
|
|
|
|
var cpp_file = FS.readFile("obj_dir/V"+topmod+".cpp", {encoding:'utf8'});
|
|
|
|
var rtn = translateVerilatorOutputToJS(h_file, cpp_file);
|
|
|
|
putWorkFile(outjs, rtn.output.code);
|
|
|
|
if (!anyTargetChanged(step, [outjs]))
|
|
|
|
return;
|
|
|
|
} catch(e) {
|
|
|
|
console.log(e);
|
2018-12-30 19:06:18 +00:00
|
|
|
errors.push({line:0,msg:""+e});
|
2018-12-15 18:14:40 +00:00
|
|
|
return {errors:errors};
|
|
|
|
}
|
2018-10-05 13:47:15 +00:00
|
|
|
//rtn.intermediate = {listing:h_file + cpp_file}; // TODO
|
2019-12-27 22:31:24 +00:00
|
|
|
var listings : CodeListingMap = {};
|
2018-06-28 00:26:22 +00:00
|
|
|
// TODO: what if found in non-top-module?
|
|
|
|
if (asmlines.length)
|
2018-10-05 13:47:15 +00:00
|
|
|
listings[step.path] = {lines:asmlines};
|
|
|
|
return {
|
|
|
|
output: rtn.output,
|
|
|
|
errors: errors,
|
2018-10-05 18:02:46 +00:00
|
|
|
listings: listings,
|
2018-10-05 13:47:15 +00:00
|
|
|
};
|
2017-11-11 19:45:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-28 00:26:22 +00:00
|
|
|
// TODO: test
|
2018-10-05 13:47:15 +00:00
|
|
|
function compileYosys(step:BuildStep) {
|
2017-11-20 01:32:58 +00:00
|
|
|
loadNative("yosys");
|
2018-06-28 00:26:22 +00:00
|
|
|
var code = step.code;
|
2017-11-20 01:32:58 +00:00
|
|
|
var errors = [];
|
2018-10-05 13:47:15 +00:00
|
|
|
var match_fn = makeErrorMatcher(errors, /ERROR: (.+?) in line (.+?[.]v):(\d+)[: ]+(.+)/i, 3, 4, step.path);
|
2017-11-20 01:32:58 +00:00
|
|
|
starttime();
|
2018-10-05 13:47:15 +00:00
|
|
|
var yosys_mod = emglobal.yosys({
|
2018-07-10 04:44:17 +00:00
|
|
|
instantiateWasm: moduleInstFn('yosys'),
|
2017-11-20 01:32:58 +00:00
|
|
|
noInitialRun:true,
|
|
|
|
print:print_fn,
|
|
|
|
printErr:match_fn,
|
|
|
|
});
|
|
|
|
endtime("create module");
|
|
|
|
var topmod = detectTopModuleName(code);
|
|
|
|
var FS = yosys_mod['FS'];
|
|
|
|
FS.writeFile(topmod+".v", code);
|
|
|
|
starttime();
|
2017-11-29 01:48:27 +00:00
|
|
|
try {
|
|
|
|
yosys_mod.callMain(["-q", "-o", topmod+".json", "-S", topmod+".v"]);
|
|
|
|
} catch (e) {
|
|
|
|
console.log(e);
|
|
|
|
endtime("compile");
|
|
|
|
return {errors:errors};
|
|
|
|
}
|
2017-11-20 01:32:58 +00:00
|
|
|
endtime("compile");
|
2018-02-03 20:20:56 +00:00
|
|
|
//TODO: filename in errors
|
2017-11-20 01:32:58 +00:00
|
|
|
if (errors.length) return {errors:errors};
|
|
|
|
try {
|
|
|
|
var json_file = FS.readFile(topmod+".json", {encoding:'utf8'});
|
|
|
|
var json = JSON.parse(json_file);
|
|
|
|
console.log(json);
|
2018-06-28 00:26:22 +00:00
|
|
|
return {yosys_json:json, errors:errors}; // TODO
|
2017-11-20 01:32:58 +00:00
|
|
|
} catch(e) {
|
|
|
|
console.log(e);
|
|
|
|
return {errors:errors};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function assembleZMAC(step:BuildStep) {
|
2018-08-28 14:10:59 +00:00
|
|
|
loadNative("zmac");
|
2018-09-27 18:26:42 +00:00
|
|
|
var hexout, lstout, binout;
|
2018-08-28 14:10:59 +00:00
|
|
|
var errors = [];
|
2018-08-28 17:59:12 +00:00
|
|
|
var params = step.params;
|
2018-08-28 14:10:59 +00:00
|
|
|
gatherFiles(step, {mainFilePath:"main.asm"});
|
2018-08-28 17:59:12 +00:00
|
|
|
var lstpath = step.prefix + ".lst";
|
2018-09-27 18:26:42 +00:00
|
|
|
var binpath = step.prefix + ".cim";
|
2018-10-01 20:18:59 +00:00
|
|
|
if (staleFiles(step, [binpath, lstpath])) {
|
2018-08-28 14:10:59 +00:00
|
|
|
/*
|
|
|
|
error1.asm(4) : 'l18d4' Undeclared
|
|
|
|
JP L18D4
|
|
|
|
|
|
|
|
error1.asm(11): warning: 'foobar' treated as label (instruction typo?)
|
|
|
|
Add a colon or move to first column to stop this warning.
|
|
|
|
1 errors (see listing if no diagnostics appeared here)
|
|
|
|
*/
|
2018-10-05 13:47:15 +00:00
|
|
|
var ZMAC = emglobal.zmac({
|
2018-08-28 14:10:59 +00:00
|
|
|
instantiateWasm: moduleInstFn('zmac'),
|
|
|
|
noInitialRun:true,
|
|
|
|
//logReadFiles:true,
|
|
|
|
print:print_fn,
|
|
|
|
printErr:makeErrorMatcher(errors, /([^( ]+)\s*[(](\d+)[)]\s*:\s*(.+)/, 2, 3, step.path),
|
|
|
|
});
|
|
|
|
var FS = ZMAC['FS'];
|
|
|
|
populateFiles(step, FS);
|
2018-08-28 17:59:12 +00:00
|
|
|
// TODO: don't know why CIM (hexary) doesn't work
|
2018-09-27 18:26:42 +00:00
|
|
|
execMain(step, ZMAC, ['-z', '-c', '--oo', 'lst,cim', step.path]);
|
2018-08-28 14:10:59 +00:00
|
|
|
if (errors.length) {
|
|
|
|
return {errors:errors};
|
|
|
|
}
|
2018-08-28 17:59:12 +00:00
|
|
|
lstout = FS.readFile("zout/"+lstpath, {encoding:'utf8'});
|
2018-09-27 18:26:42 +00:00
|
|
|
binout = FS.readFile("zout/"+binpath, {encoding:'binary'});
|
|
|
|
putWorkFile(binpath, binout);
|
2018-08-28 14:10:59 +00:00
|
|
|
putWorkFile(lstpath, lstout);
|
2018-09-27 18:26:42 +00:00
|
|
|
if (!anyTargetChanged(step, [binpath, lstpath]))
|
2018-08-28 17:59:12 +00:00
|
|
|
return;
|
|
|
|
// 230: 1739+7+x 017A 1600 L017A: LD D,00h
|
2018-09-03 13:37:17 +00:00
|
|
|
var lines = parseListing(lstout, /\s*(\d+):\s*([0-9a-f]+)\s+([0-9a-f]+)\s+(.+)/i, 1, 2, 3);
|
2019-12-27 22:31:24 +00:00
|
|
|
var listings : CodeListingMap = {};
|
2018-08-28 17:59:12 +00:00
|
|
|
listings[lstpath] = {lines:lines};
|
|
|
|
// parse symbol table
|
|
|
|
var symbolmap = {};
|
|
|
|
var sympos = lstout.indexOf('Symbol Table:');
|
|
|
|
if (sympos > 0) {
|
|
|
|
var symout = lstout.slice(sympos+14);
|
|
|
|
symout.split('\n').forEach(function(l) {
|
|
|
|
var m = l.match(/(\S+)\s+([= ]*)([0-9a-f]+)/i);
|
|
|
|
if (m) {
|
|
|
|
symbolmap[m[1]] = parseInt(m[3],16);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2018-08-28 14:10:59 +00:00
|
|
|
return {
|
2018-09-27 18:26:42 +00:00
|
|
|
output:binout,
|
2018-08-28 14:10:59 +00:00
|
|
|
listings:listings,
|
|
|
|
errors:errors,
|
2018-08-28 17:59:12 +00:00
|
|
|
symbolmap:symbolmap
|
2018-08-28 14:10:59 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-20 19:10:04 +00:00
|
|
|
function preprocessBatariBasic(code:string) : string {
|
|
|
|
load("bbpreprocess");
|
|
|
|
var bbout = "";
|
|
|
|
function addbbout_fn(s) {
|
|
|
|
bbout += s;
|
|
|
|
bbout += "\n";
|
|
|
|
}
|
|
|
|
var BBPRE = 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;
|
|
|
|
}
|
|
|
|
|
2018-11-20 17:31:19 +00:00
|
|
|
function compileBatariBasic(step:BuildStep) {
|
|
|
|
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 = emglobal.bb2600basic({
|
|
|
|
noInitialRun:true,
|
|
|
|
//logReadFiles:true,
|
|
|
|
print:addasmout_fn,
|
|
|
|
printErr:match_fn,
|
|
|
|
noFSInit:true,
|
2018-11-20 19:39:33 +00:00
|
|
|
TOTAL_MEMORY:64*1024*1024,
|
2018-11-20 17:31:19 +00:00
|
|
|
});
|
|
|
|
var FS = BB['FS'];
|
|
|
|
populateFiles(step, FS);
|
2018-11-20 19:10:04 +00:00
|
|
|
// preprocess, pipe file to stdin
|
2018-11-28 16:10:24 +00:00
|
|
|
var code = getWorkFileAsString(step.path);
|
2018-11-20 19:10:04 +00:00
|
|
|
code = preprocessBatariBasic(code);
|
2018-11-20 17:31:19 +00:00
|
|
|
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 = "";
|
2018-11-20 19:10:04 +00:00
|
|
|
var splitasm = asmout.split("bB.asm file is split here");
|
2018-11-20 17:31:19 +00:00
|
|
|
for (var incfile of includes) {
|
2018-11-20 19:10:04 +00:00
|
|
|
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'});
|
2018-11-20 17:31:19 +00:00
|
|
|
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],
|
2018-11-21 12:21:07 +00:00
|
|
|
files:[destpath, "2600basic.h", "2600basic_variable_redefs.h"],
|
|
|
|
bblines:true,
|
2018-11-20 17:31:19 +00:00
|
|
|
};
|
|
|
|
}
|
2018-08-28 14:10:59 +00:00
|
|
|
|
2018-11-21 16:53:33 +00:00
|
|
|
function setupRequireFunction() {
|
|
|
|
var exports = {};
|
|
|
|
exports['jsdom'] = {
|
|
|
|
JSDOM: function(a,b) {
|
|
|
|
this.window = {};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
emglobal['require'] = (modname:string) => {
|
|
|
|
console.log('require',modname);
|
|
|
|
return exports[modname];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function translateShowdown(step:BuildStep) {
|
|
|
|
setupRequireFunction();
|
|
|
|
load("showdown.min");
|
|
|
|
var showdown = emglobal['showdown'];
|
|
|
|
var converter = new showdown.Converter({
|
|
|
|
tables:'true',
|
|
|
|
smoothLivePreview:'true',
|
|
|
|
requireSpaceBeforeHeadingText:'true',
|
|
|
|
emoji:'true',
|
|
|
|
});
|
2018-11-28 16:10:24 +00:00
|
|
|
var code = getWorkFileAsString(step.path);
|
2018-11-21 16:53:33 +00:00
|
|
|
var html = converter.makeHtml(code);
|
|
|
|
delete emglobal['require'];
|
|
|
|
return {
|
|
|
|
output:html
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-08-06 19:36:28 +00:00
|
|
|
// http://datapipe-blackbeltsystems.com/windows/flex/asm09.html
|
2019-03-21 01:38:53 +00:00
|
|
|
function assembleXASM6809(step:BuildStep) {
|
|
|
|
load("xasm6809");
|
|
|
|
var alst = "";
|
|
|
|
var lasterror = null;
|
|
|
|
var errors = [];
|
|
|
|
function match_fn(s) {
|
|
|
|
alst += s;
|
|
|
|
alst += "\n";
|
|
|
|
if (lasterror) {
|
2020-06-30 17:10:15 +00:00
|
|
|
var line = parseInt(s.slice(0,5)) || 0;
|
2019-03-21 01:38:53 +00:00
|
|
|
errors.push({
|
|
|
|
line:line,
|
|
|
|
msg:lasterror
|
|
|
|
});
|
|
|
|
lasterror = null;
|
|
|
|
}
|
|
|
|
else if (s.startsWith("***** ")) {
|
|
|
|
lasterror = s.slice(6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var Module = emglobal.xasm6809({
|
|
|
|
noInitialRun:true,
|
|
|
|
//logReadFiles:true,
|
|
|
|
print:match_fn,
|
|
|
|
printErr:print_fn
|
|
|
|
});
|
|
|
|
var FS = Module['FS'];
|
|
|
|
//setupFS(FS);
|
2019-08-06 19:36:28 +00:00
|
|
|
populateFiles(step, FS, {
|
|
|
|
mainFilePath:'main.asm'
|
|
|
|
});
|
|
|
|
var binpath = step.prefix + '.bin';
|
|
|
|
var lstpath = step.prefix + '.lst'; // in stdout
|
|
|
|
Module.callMain(["-c", "-l", "-s", "-y", "-o="+binpath, step.path]);
|
2019-03-21 01:38:53 +00:00
|
|
|
if (errors.length)
|
|
|
|
return {errors:errors};
|
2019-08-06 19:36:28 +00:00
|
|
|
var aout = FS.readFile(binpath, {encoding:'binary'});
|
|
|
|
if (aout.length == 0) {
|
|
|
|
console.log(alst);
|
|
|
|
errors.push({line:0, msg:"Empty output file"});
|
|
|
|
return {errors:errors};
|
|
|
|
}
|
|
|
|
putWorkFile(binpath, aout);
|
|
|
|
putWorkFile(lstpath, alst);
|
|
|
|
// TODO: symbol map
|
|
|
|
//mond09 0000
|
|
|
|
var symbolmap = {};
|
|
|
|
//00005 W 0003 [ 8] A6890011 lda >PALETTE,x
|
|
|
|
//00012 0011 0C0203 fcb 12,2,3
|
|
|
|
var asmlines = parseListing(alst, /^\s*([0-9]+) .+ ([0-9A-F]+)\s+\[([0-9 ]+)\]\s+([0-9A-F]+) (.*)/i, 1, 2, 4, 3);
|
2019-12-27 22:31:24 +00:00
|
|
|
var listings : CodeListingMap = {};
|
2019-08-06 19:36:28 +00:00
|
|
|
listings[step.prefix+'.lst'] = {lines:asmlines, text:alst};
|
2019-03-21 01:38:53 +00:00
|
|
|
return {
|
2019-08-06 19:36:28 +00:00
|
|
|
output:aout,
|
|
|
|
listings:listings,
|
|
|
|
errors:errors,
|
|
|
|
symbolmap:symbolmap,
|
2019-03-21 01:38:53 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-08-16 01:25:08 +00:00
|
|
|
// http://www.nespowerpak.com/nesasm/
|
|
|
|
function assembleNESASM(step:BuildStep) {
|
|
|
|
loadNative("nesasm");
|
2019-08-26 21:39:14 +00:00
|
|
|
var re_filename = /\#\[(\d+)\]\s+(\S+)/;
|
2019-08-16 01:25:08 +00:00
|
|
|
var re_insn = /\s+(\d+)\s+([0-9A-F]+):([0-9A-F]+)/;
|
|
|
|
var re_error = /\s+(.+)/;
|
2019-08-26 21:39:14 +00:00
|
|
|
var errors : WorkerError[] = [];
|
2019-08-16 01:25:08 +00:00
|
|
|
var state = 0;
|
|
|
|
var lineno = 0;
|
|
|
|
var filename;
|
|
|
|
function match_fn(s) {
|
|
|
|
var m;
|
|
|
|
switch (state) {
|
|
|
|
case 0:
|
|
|
|
m = re_filename.exec(s);
|
|
|
|
if (m) {
|
|
|
|
filename = m[2];
|
|
|
|
}
|
|
|
|
m = re_insn.exec(s);
|
|
|
|
if (m) {
|
|
|
|
lineno = parseInt(m[1]);
|
|
|
|
state = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
m = re_error.exec(s);
|
|
|
|
if (m) {
|
2019-08-26 21:39:14 +00:00
|
|
|
errors.push({path:filename, line:lineno, msg:m[1]});
|
2019-08-16 01:25:08 +00:00
|
|
|
state = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var Module = emglobal.nesasm({
|
|
|
|
instantiateWasm: moduleInstFn('nesasm'),
|
|
|
|
noInitialRun:true,
|
|
|
|
print:match_fn
|
|
|
|
});
|
|
|
|
var FS = Module['FS'];
|
|
|
|
populateFiles(step, FS, {
|
|
|
|
mainFilePath:'main.a'
|
|
|
|
});
|
|
|
|
var binpath = step.prefix+'.nes';
|
|
|
|
var lstpath = step.prefix+'.lst';
|
|
|
|
var sympath = step.prefix+'.fns';
|
|
|
|
execMain(step, Module, [step.path, '-s', "-l", "2" ]);
|
|
|
|
// parse main listing, get errors and listings for each file
|
2019-12-27 22:31:24 +00:00
|
|
|
var listings : CodeListingMap = {};
|
2019-08-16 01:25:08 +00:00
|
|
|
try {
|
|
|
|
var alst = FS.readFile(lstpath, {'encoding':'utf8'});
|
|
|
|
// 16 00:C004 8E 17 40 STX $4017 ; disable APU frame IRQ
|
|
|
|
var asmlines = parseListing(alst, /^\s*(\d+)\s+([0-9A-F]+):([0-9A-F]+)\s+([0-9A-F ]+?) (.*)/i, 1, 3, 4);
|
|
|
|
putWorkFile(lstpath, alst);
|
|
|
|
listings[lstpath] = {
|
|
|
|
lines:asmlines,
|
|
|
|
text:alst
|
|
|
|
};
|
|
|
|
} catch (e) {
|
|
|
|
//
|
|
|
|
}
|
|
|
|
if (errors.length) {
|
|
|
|
return {errors:errors};
|
|
|
|
}
|
|
|
|
// read binary rom output and symbols
|
|
|
|
var aout, asym;
|
|
|
|
aout = FS.readFile(binpath);
|
|
|
|
try {
|
|
|
|
asym = FS.readFile(sympath, {'encoding':'utf8'});
|
|
|
|
} catch (e) {
|
|
|
|
console.log(e);
|
2020-07-15 20:41:09 +00:00
|
|
|
errors.push({line:0,msg:"No symbol table generated, maybe missing ENDM or segment overflow?"});
|
2019-08-16 01:25:08 +00:00
|
|
|
return {errors:errors}
|
|
|
|
}
|
|
|
|
putWorkFile(binpath, aout);
|
|
|
|
putWorkFile(sympath, asym);
|
|
|
|
if (alst) putWorkFile(lstpath, alst); // listing optional (use LIST)
|
|
|
|
// return unchanged if no files changed
|
|
|
|
if (!anyTargetChanged(step, [binpath, sympath]))
|
|
|
|
return;
|
|
|
|
// parse symbols
|
|
|
|
var symbolmap = {};
|
|
|
|
for (var s of asym.split("\n")) {
|
|
|
|
if (!s.startsWith(';')) {
|
|
|
|
var m = /(\w+)\s+=\s+[$]([0-9A-F]+)/.exec(s);
|
|
|
|
if (m) {
|
|
|
|
symbolmap[m[1]] = parseInt(m[2], 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
output:aout,
|
|
|
|
listings:listings,
|
|
|
|
errors:errors,
|
|
|
|
symbolmap:symbolmap,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-12-07 23:20:01 +00:00
|
|
|
function compileCMOC(step:BuildStep) {
|
|
|
|
loadNative("cmoc");
|
|
|
|
var params = step.params;
|
|
|
|
// stderr
|
2020-06-10 15:25:42 +00:00
|
|
|
var re_err1 = /^[/]*([^:]*):(\d+): (.+)$/;
|
2019-12-07 23:20:01 +00:00
|
|
|
var errors : WorkerError[] = [];
|
|
|
|
var errline = 0;
|
|
|
|
function match_fn(s) {
|
|
|
|
var matches = re_err1.exec(s);
|
|
|
|
if (matches) {
|
|
|
|
errors.push({
|
2019-12-11 03:19:12 +00:00
|
|
|
line:parseInt(matches[2]),
|
|
|
|
msg:matches[3],
|
|
|
|
path:matches[1] || step.path
|
2019-12-07 23:20:01 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
console.log(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
gatherFiles(step, {mainFilePath:"main.c"});
|
|
|
|
var destpath = step.prefix + '.s';
|
|
|
|
if (staleFiles(step, [destpath])) {
|
|
|
|
var args = ['-S', '-Werror', '-V',
|
|
|
|
'-I/share/include',
|
|
|
|
'-I.',
|
|
|
|
step.path];
|
|
|
|
var CMOC = emglobal.cmoc({
|
|
|
|
instantiateWasm: moduleInstFn('cmoc'),
|
|
|
|
noInitialRun:true,
|
|
|
|
//logReadFiles:true,
|
|
|
|
print:match_fn,
|
|
|
|
printErr:match_fn,
|
|
|
|
});
|
2019-12-11 03:19:12 +00:00
|
|
|
// load source file and preprocess
|
|
|
|
var code = getWorkFileAsString(step.path);
|
|
|
|
var preproc = preprocessMCPP(step, null);
|
|
|
|
if (preproc.errors) return preproc;
|
|
|
|
else code = preproc.code;
|
|
|
|
// set up filesystem
|
2019-12-07 23:20:01 +00:00
|
|
|
var FS = CMOC['FS'];
|
|
|
|
//setupFS(FS, '65-'+getRootBasePlatform(step.platform));
|
|
|
|
populateFiles(step, FS);
|
2019-12-11 03:19:12 +00:00
|
|
|
FS.writeFile(step.path, code);
|
2019-12-07 23:20:01 +00:00
|
|
|
fixParamsWithDefines(step.path, params);
|
2020-06-09 15:05:46 +00:00
|
|
|
if (params.extra_compile_args) {
|
|
|
|
args.unshift.apply(args, params.extra_compile_args);
|
|
|
|
}
|
2019-12-07 23:20:01 +00:00
|
|
|
execMain(step, CMOC, args);
|
|
|
|
if (errors.length)
|
|
|
|
return {errors:errors};
|
|
|
|
var asmout = FS.readFile(destpath, {encoding:'utf8'});
|
|
|
|
putWorkFile(destpath, asmout);
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
nexttool:"lwasm",
|
|
|
|
path:destpath,
|
|
|
|
args:[destpath],
|
|
|
|
files:[destpath],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function assembleLWASM(step:BuildStep) {
|
|
|
|
loadNative("lwasm");
|
|
|
|
var errors = [];
|
|
|
|
gatherFiles(step, {mainFilePath:"main.s"});
|
|
|
|
var objpath = step.prefix+".o";
|
|
|
|
var lstpath = step.prefix+".lst";
|
|
|
|
if (staleFiles(step, [objpath, lstpath])) {
|
|
|
|
var objout, lstout;
|
|
|
|
var args = ['-9', '--obj', '-I/share/asminc', '-o'+objpath, '-l'+lstpath, step.path];
|
|
|
|
var LWASM = emglobal.lwasm({
|
|
|
|
instantiateWasm: moduleInstFn('lwasm'),
|
|
|
|
noInitialRun:true,
|
|
|
|
//logReadFiles:true,
|
|
|
|
print:print_fn,
|
|
|
|
printErr:msvcErrorMatcher(errors),
|
|
|
|
});
|
|
|
|
var FS = LWASM['FS'];
|
|
|
|
//setupFS(FS, '65-'+getRootBasePlatform(step.platform));
|
|
|
|
populateFiles(step, FS);
|
|
|
|
fixParamsWithDefines(step.path, step.params);
|
|
|
|
execMain(step, LWASM, args);
|
|
|
|
if (errors.length)
|
|
|
|
return {errors:errors};
|
|
|
|
objout = FS.readFile(objpath, {encoding:'binary'});
|
|
|
|
lstout = FS.readFile(lstpath, {encoding:'utf8'});
|
|
|
|
putWorkFile(objpath, objout);
|
|
|
|
putWorkFile(lstpath, lstout);
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
linktool:"lwlink",
|
|
|
|
files:[objpath, lstpath],
|
|
|
|
args:[objpath]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function linkLWLINK(step:BuildStep) {
|
|
|
|
loadNative("lwlink");
|
|
|
|
var params = step.params;
|
|
|
|
gatherFiles(step);
|
|
|
|
var binpath = "main";
|
|
|
|
if (staleFiles(step, [binpath])) {
|
|
|
|
var errors = [];
|
|
|
|
var LWLINK = emglobal.lwlink({
|
|
|
|
instantiateWasm: moduleInstFn('lwlink'),
|
|
|
|
noInitialRun:true,
|
|
|
|
//logReadFiles:true,
|
|
|
|
print:print_fn,
|
2019-12-11 03:19:12 +00:00
|
|
|
printErr:function(s) {
|
|
|
|
if (s.startsWith("Warning:"))
|
|
|
|
console.log(s);
|
|
|
|
else
|
|
|
|
errors.push({msg:s,line:0});
|
|
|
|
}
|
2019-12-07 23:20:01 +00:00
|
|
|
});
|
|
|
|
var FS = LWLINK['FS'];
|
|
|
|
//setupFS(FS, '65-'+getRootBasePlatform(step.platform));
|
|
|
|
populateFiles(step, FS);
|
|
|
|
populateExtraFiles(step, FS, params.extra_link_files);
|
|
|
|
var libargs = params.extra_link_args || [];
|
|
|
|
var args = [
|
|
|
|
'-L.',
|
2019-12-11 03:19:12 +00:00
|
|
|
'--entry=program_start',
|
2020-06-09 22:32:29 +00:00
|
|
|
'--raw',
|
2019-12-11 03:19:12 +00:00
|
|
|
'--output=main',
|
|
|
|
'--map=main.map'].concat(libargs, step.args);
|
2019-12-07 23:20:01 +00:00
|
|
|
console.log(args);
|
|
|
|
execMain(step, LWLINK, args);
|
|
|
|
if (errors.length)
|
|
|
|
return {errors:errors};
|
|
|
|
var aout = FS.readFile("main", {encoding:'binary'});
|
|
|
|
var mapout = FS.readFile("main.map", {encoding:'utf8'});
|
|
|
|
putWorkFile("main", aout);
|
|
|
|
putWorkFile("main.map", mapout);
|
|
|
|
// return unchanged if no files changed
|
|
|
|
if (!anyTargetChanged(step, ["main", "main.map"]))
|
|
|
|
return;
|
2020-06-09 22:32:29 +00:00
|
|
|
// parse symbol map
|
|
|
|
//console.log(mapout);
|
2019-12-07 23:20:01 +00:00
|
|
|
var symbolmap = {};
|
2020-06-09 22:32:29 +00:00
|
|
|
var segments = [];
|
|
|
|
for (var s of mapout.split("\n")) {
|
|
|
|
var toks = s.split(" ");
|
|
|
|
// TODO: use regex
|
|
|
|
if (toks[0] == 'Symbol:') {
|
|
|
|
let ident = toks[1];
|
|
|
|
let ofs = parseInt(toks[4], 16);
|
2020-06-10 15:25:42 +00:00
|
|
|
if (ident && ofs >= 0 && !ident.startsWith("l_") && !/^L\d+$/.test(ident)) {
|
2020-06-09 22:32:29 +00:00
|
|
|
symbolmap[ident] = ofs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (toks[0] == 'Section:') {
|
|
|
|
let seg = toks[1];
|
|
|
|
let segstart = parseInt(toks[5], 16);
|
|
|
|
let segsize = parseInt(toks[7], 16);
|
|
|
|
segments.push({name:seg, start:segstart, size:segsize});
|
|
|
|
}
|
|
|
|
}
|
2019-12-07 23:20:01 +00:00
|
|
|
// build listings
|
2019-12-27 22:31:24 +00:00
|
|
|
var listings : CodeListingMap = {};
|
2019-12-07 23:20:01 +00:00
|
|
|
for (var fn of step.files) {
|
|
|
|
if (fn.endsWith('.lst')) {
|
|
|
|
// TODO
|
|
|
|
var lstout = FS.readFile(fn, {encoding:'utf8'});
|
2019-12-11 03:19:12 +00:00
|
|
|
var asmlines = parseListing(lstout, /^([0-9A-F]+)\s+([0-9A-F]+)\s+[(]\s*(.+?)[)]:(\d+) (.*)/i, 4, 1, 2, 3);
|
2020-06-10 15:25:42 +00:00
|
|
|
// * Line //threed.c:117: init of variable e
|
|
|
|
var srclines = parseSourceLines(lstout, /Line .+?:(\d+)/i, /^([0-9A-F]{4})/i);
|
2019-12-07 23:20:01 +00:00
|
|
|
putWorkFile(fn, lstout);
|
2019-12-28 19:49:09 +00:00
|
|
|
// TODO: you have to get rid of all source lines to get asm listing
|
2019-12-07 23:20:01 +00:00
|
|
|
listings[fn] = {
|
|
|
|
asmlines:srclines.length ? asmlines : null,
|
|
|
|
lines:srclines.length ? srclines : asmlines,
|
|
|
|
text:lstout
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
output:aout, //.slice(0),
|
|
|
|
listings:listings,
|
|
|
|
errors:errors,
|
|
|
|
symbolmap:symbolmap,
|
|
|
|
segments:segments
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2019-03-21 01:38:53 +00:00
|
|
|
|
2020-06-14 01:28:58 +00:00
|
|
|
// http://www.techhelpmanual.com/829-program_startup___exit.html
|
|
|
|
function compileSmallerC(step:BuildStep) {
|
|
|
|
loadNative("smlrc");
|
|
|
|
var params = step.params;
|
|
|
|
// stderr
|
|
|
|
var re_err1 = /^Error in "[/]*(.+)" [(](\d+):(\d+)[)]/;
|
|
|
|
var errors : WorkerError[] = [];
|
|
|
|
var errline = 0;
|
|
|
|
var errpath = step.path;
|
|
|
|
function match_fn(s) {
|
|
|
|
var matches = re_err1.exec(s);
|
|
|
|
if (matches) {
|
|
|
|
errline = parseInt(matches[2]);
|
|
|
|
errpath = matches[1];
|
|
|
|
} else {
|
|
|
|
errors.push({
|
|
|
|
line:errline,
|
|
|
|
msg:s,
|
|
|
|
path:errpath,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
gatherFiles(step, {mainFilePath:"main.c"});
|
|
|
|
var destpath = step.prefix + '.asm';
|
|
|
|
if (staleFiles(step, [destpath])) {
|
|
|
|
var args = ['-seg16',
|
|
|
|
//'-nobss',
|
|
|
|
'-no-externs',
|
|
|
|
step.path, destpath];
|
|
|
|
var smlrc = emglobal.smlrc({
|
|
|
|
instantiateWasm: moduleInstFn('smlrc'),
|
|
|
|
noInitialRun:true,
|
|
|
|
//logReadFiles:true,
|
|
|
|
print:match_fn,
|
|
|
|
printErr:match_fn,
|
|
|
|
});
|
|
|
|
// load source file and preprocess
|
|
|
|
var code = getWorkFileAsString(step.path);
|
|
|
|
var preproc = preprocessMCPP(step, null);
|
|
|
|
if (preproc.errors) return preproc;
|
|
|
|
else code = preproc.code;
|
|
|
|
// set up filesystem
|
|
|
|
var FS = smlrc['FS'];
|
|
|
|
//setupFS(FS, '65-'+getRootBasePlatform(step.platform));
|
|
|
|
populateFiles(step, FS);
|
|
|
|
FS.writeFile(step.path, code);
|
|
|
|
fixParamsWithDefines(step.path, params);
|
|
|
|
if (params.extra_compile_args) {
|
|
|
|
args.unshift.apply(args, params.extra_compile_args);
|
|
|
|
}
|
|
|
|
execMain(step, smlrc, args);
|
|
|
|
if (errors.length)
|
|
|
|
return {errors:errors};
|
|
|
|
var asmout = FS.readFile(destpath, {encoding:'utf8'});
|
|
|
|
putWorkFile(destpath, asmout);
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
nexttool:"yasm",
|
|
|
|
path:destpath,
|
|
|
|
args:[destpath],
|
|
|
|
files:[destpath],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
function assembleYASM(step:BuildStep) {
|
|
|
|
loadNative("yasm");
|
|
|
|
var errors = [];
|
|
|
|
gatherFiles(step, {mainFilePath:"main.asm"});
|
|
|
|
var objpath = step.prefix+".exe";
|
|
|
|
var lstpath = step.prefix+".lst";
|
|
|
|
var mappath = step.prefix+".map";
|
|
|
|
if (staleFiles(step, [objpath])) {
|
|
|
|
var args = [ '-X', 'vc',
|
|
|
|
'-a', 'x86', '-f', 'dosexe', '-p', 'nasm',
|
|
|
|
'-D', 'freedos',
|
|
|
|
//'-g', 'dwarf2',
|
|
|
|
//'-I/share/asminc',
|
|
|
|
'-o', objpath, '-l', lstpath, '--mapfile='+mappath,
|
|
|
|
step.path];
|
|
|
|
// return yasm/*.ready*/
|
|
|
|
var YASM = emglobal.yasm({
|
|
|
|
instantiateWasm: moduleInstFn('yasm'),
|
|
|
|
noInitialRun:true,
|
|
|
|
//logReadFiles:true,
|
|
|
|
print:print_fn,
|
|
|
|
printErr:msvcErrorMatcher(errors),
|
|
|
|
});
|
|
|
|
var FS = YASM['FS'];
|
|
|
|
//setupFS(FS, '65-'+getRootBasePlatform(step.platform));
|
|
|
|
populateFiles(step, FS);
|
|
|
|
//fixParamsWithDefines(step.path, step.params);
|
|
|
|
execMain(step, YASM, args);
|
|
|
|
if (errors.length)
|
|
|
|
return {errors:errors};
|
|
|
|
var objout, lstout, mapout;
|
|
|
|
objout = FS.readFile(objpath, {encoding:'binary'});
|
|
|
|
lstout = FS.readFile(lstpath, {encoding:'utf8'});
|
|
|
|
mapout = FS.readFile(mappath, {encoding:'utf8'});
|
|
|
|
putWorkFile(objpath, objout);
|
|
|
|
putWorkFile(lstpath, lstout);
|
|
|
|
//putWorkFile(mappath, mapout);
|
|
|
|
if (!anyTargetChanged(step, [objpath]))
|
|
|
|
return;
|
|
|
|
var symbolmap = {};
|
|
|
|
var segments = [];
|
|
|
|
var lines = parseListing(lstout, /\s*(\d+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+(.+)/i, 1, 2, 3);
|
|
|
|
var listings : CodeListingMap = {};
|
|
|
|
listings[lstpath] = {lines:lines, text:lstout};
|
|
|
|
return {
|
|
|
|
output:objout, //.slice(0),
|
|
|
|
listings:listings,
|
|
|
|
errors:errors,
|
|
|
|
symbolmap:symbolmap,
|
|
|
|
segments:segments
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-06 23:53:20 +00:00
|
|
|
interface XMLNode {
|
|
|
|
type: string;
|
|
|
|
text: string | null;
|
|
|
|
children: XMLNode[];
|
|
|
|
}
|
|
|
|
|
|
|
|
function parseXMLPoorly(s: string) : XMLNode {
|
|
|
|
var re = /[<]([/]?)([?a-z_-]+)([^>]*)[>]+|(\s*[^<]+)/gi;
|
2020-07-08 01:56:44 +00:00
|
|
|
var m : RegExpMatchArray;
|
|
|
|
//var i=0;
|
|
|
|
var stack : XMLNode[] = [];
|
2020-07-06 23:53:20 +00:00
|
|
|
while (m = re.exec(s)) {
|
|
|
|
var [_m0,close,ident,attrs,content] = m;
|
|
|
|
//if (i++<100) console.log(close,ident,attrs,content);
|
|
|
|
if (close) {
|
|
|
|
var top = stack.pop();
|
|
|
|
if (top.type != ident) throw "mismatch close tag: " + ident;
|
|
|
|
stack[stack.length-1].children.push(top);
|
|
|
|
} else if (ident) {
|
|
|
|
stack.push({type:ident, text:null, children:[]});
|
|
|
|
} else if (content != null) {
|
|
|
|
stack[stack.length-1].text = (content as string).trim();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return top;
|
|
|
|
}
|
|
|
|
|
|
|
|
function compileInform6(step:BuildStep) {
|
|
|
|
loadNative("inform");
|
|
|
|
var errors = [];
|
|
|
|
gatherFiles(step, {mainFilePath:"main.inf"});
|
|
|
|
var objpath = step.prefix+".z5";
|
|
|
|
if (staleFiles(step, [objpath])) {
|
|
|
|
var errorMatcher = msvcErrorMatcher(errors);
|
|
|
|
var lstout = "";
|
|
|
|
var match_fn = (s: string) => {
|
|
|
|
if (s.indexOf("Error:") >= 0) {
|
|
|
|
errorMatcher(s);
|
|
|
|
} else {
|
|
|
|
lstout += s;
|
|
|
|
lstout += "\n";
|
|
|
|
}
|
|
|
|
}
|
2020-07-08 01:56:44 +00:00
|
|
|
var args = [ '-afjnops', '-v5', '-Cu', '-E1', '-k', '+/share/lib', step.path ];
|
2020-07-06 23:53:20 +00:00
|
|
|
var inform = emglobal.inform({
|
|
|
|
instantiateWasm: moduleInstFn('inform'),
|
|
|
|
noInitialRun:true,
|
|
|
|
//logReadFiles:true,
|
|
|
|
print:match_fn,
|
|
|
|
printErr:match_fn,
|
|
|
|
});
|
|
|
|
var FS = inform['FS'];
|
|
|
|
setupFS(FS, 'inform');
|
|
|
|
populateFiles(step, FS);
|
|
|
|
//fixParamsWithDefines(step.path, step.params);
|
|
|
|
execMain(step, inform, args);
|
|
|
|
if (errors.length)
|
|
|
|
return {errors:errors};
|
|
|
|
var objout = FS.readFile(objpath, {encoding:'binary'});
|
|
|
|
putWorkFile(objpath, objout);
|
|
|
|
if (!anyTargetChanged(step, [objpath]))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// parse debug XML
|
|
|
|
var symbolmap = {};
|
2020-07-08 01:56:44 +00:00
|
|
|
var segments : Segment[] = [];
|
|
|
|
var entitymap = {
|
|
|
|
// number -> string
|
|
|
|
'object':{}, 'property':{}, 'attribute':{}, 'constant':{}, 'global-variable':{}, 'routine':{},
|
|
|
|
};
|
2020-07-06 23:53:20 +00:00
|
|
|
var dbgout = FS.readFile("gameinfo.dbg", {encoding:'utf8'});
|
|
|
|
var xmlroot = parseXMLPoorly(dbgout);
|
|
|
|
//console.log(xmlroot);
|
2020-07-08 01:56:44 +00:00
|
|
|
var segtype = "ram";
|
2020-07-06 23:53:20 +00:00
|
|
|
xmlroot.children.forEach((node) => {
|
|
|
|
switch (node.type) {
|
|
|
|
case 'global-variable':
|
|
|
|
case 'routine':
|
|
|
|
var ident = node.children.find((c,v) => c.type=='identifier').text;
|
|
|
|
var address = parseInt(node.children.find((c,v) => c.type=='address').text);
|
|
|
|
symbolmap[ident] = address;
|
2020-07-08 01:56:44 +00:00
|
|
|
entitymap[node.type][address] = ident;
|
2020-07-06 23:53:20 +00:00
|
|
|
break;
|
|
|
|
case 'object':
|
|
|
|
case 'property':
|
2020-07-08 01:56:44 +00:00
|
|
|
case 'attribute':
|
2020-07-06 23:53:20 +00:00
|
|
|
var ident = node.children.find((c,v) => c.type=='identifier').text;
|
|
|
|
var value = parseInt(node.children.find((c,v) => c.type=='value').text);
|
2020-07-08 01:56:44 +00:00
|
|
|
//entitymap[node.type][ident] = value;
|
|
|
|
entitymap[node.type][value] = ident;
|
2020-07-06 23:53:20 +00:00
|
|
|
//symbolmap[ident] = address | 0x1000000;
|
|
|
|
break;
|
2020-07-08 01:56:44 +00:00
|
|
|
case 'story-file-section':
|
|
|
|
var name = node.children.find((c,v) => c.type=='type').text;
|
|
|
|
var address = parseInt(node.children.find((c,v) => c.type=='address').text);
|
|
|
|
var endAddress = parseInt(node.children.find((c,v) => c.type=='end-address').text);
|
|
|
|
if (name == "grammar table") segtype = "rom";
|
|
|
|
segments.push({name:name, start:address, size:endAddress-address, type:segtype});
|
2020-07-06 23:53:20 +00:00
|
|
|
}
|
|
|
|
});
|
2020-07-08 01:56:44 +00:00
|
|
|
// parse listing
|
2020-07-06 23:53:20 +00:00
|
|
|
var listings : CodeListingMap = {};
|
|
|
|
// 35 +00015 <*> call_vs long_19 location long_424 -> sp
|
|
|
|
var lines = parseListing(lstout, /\s*(\d+)\s+[+]([0-9a-f]+)\s+([<*>]*)\s*(\w+)\s+(.+)/i, -1, 2, 4);
|
|
|
|
var lstpath = step.prefix + '.lst';
|
|
|
|
listings[lstpath] = {lines:[], asmlines:lines, text:lstout};
|
|
|
|
return {
|
|
|
|
output:objout, //.slice(0),
|
|
|
|
listings:listings,
|
|
|
|
errors:errors,
|
|
|
|
symbolmap:symbolmap,
|
|
|
|
segments:segments,
|
2020-07-08 01:56:44 +00:00
|
|
|
debuginfo:entitymap,
|
2020-07-06 23:53:20 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2020-07-08 01:56:44 +00:00
|
|
|
|
2020-07-25 23:33:55 +00:00
|
|
|
/*
|
|
|
|
------+-------------------+-------------+----+---------+------+-----------------------+-------------------------------------------------------------------
|
|
|
|
Line | # File Line | Line Type | MX | Reloc | Size | Address Object Code | Source Code
|
|
|
|
------+-------------------+-------------+----+---------+------+-----------------------+-------------------------------------------------------------------
|
|
|
|
1 | 1 zap.asm 1 | Unknown | ?? | | -1 | 00/FFFF | broak
|
|
|
|
2 | 1 zap.asm 2 | Comment | ?? | | -1 | 00/FFFF | * SPACEGAME
|
|
|
|
|
|
|
|
=> [Error] Impossible to decode address mode for instruction 'BNE KABOOM!' (line 315, file 'zap.asm') : The number of element in 'KABOOM!' is even (should be value [operator value [operator value]...]).
|
|
|
|
=> [Error] Unknown line 'foo' in source file 'zap.asm' (line 315)
|
|
|
|
=> Creating Object file 'pcs.bin'
|
|
|
|
=> Creating Output file 'pcs.bin_S01__Output.txt'
|
|
|
|
|
|
|
|
*/
|
|
|
|
function assembleMerlin32(step:BuildStep) {
|
|
|
|
loadNative("merlin32");
|
|
|
|
var errors = [];
|
|
|
|
var lstfiles = [];
|
|
|
|
gatherFiles(step, {mainFilePath:"main.lnk"});
|
|
|
|
var objpath = step.prefix+".bin";
|
|
|
|
if (staleFiles(step, [objpath])) {
|
|
|
|
var args = [ '-v', step.path ];
|
|
|
|
var merlin32 = emglobal.merlin32({
|
|
|
|
instantiateWasm: moduleInstFn('merlin32'),
|
|
|
|
noInitialRun:true,
|
|
|
|
print:(s:string) => {
|
|
|
|
var m = /\s*=>\s*Creating Output file '(.+?)'/.exec(s);
|
|
|
|
if (m) {
|
|
|
|
lstfiles.push(m[1]);
|
|
|
|
}
|
|
|
|
var errpos = s.indexOf('Error');
|
|
|
|
if (errpos >= 0) {
|
|
|
|
s = s.slice(errpos+6).trim();
|
|
|
|
var mline = /\bline (\d+)\b/.exec(s);
|
|
|
|
var mpath = /\bfile '(.+?)'/.exec(s);
|
|
|
|
errors.push({
|
|
|
|
line:parseInt(mline[1]) || 0,
|
|
|
|
msg:s,
|
|
|
|
path:mpath[1] || step.path,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
printErr:print_fn,
|
|
|
|
});
|
|
|
|
var FS = merlin32['FS'];
|
|
|
|
populateFiles(step, FS);
|
|
|
|
execMain(step, merlin32, args);
|
|
|
|
if (errors.length)
|
|
|
|
return {errors:errors};
|
|
|
|
|
|
|
|
var errout = null;
|
|
|
|
try {
|
|
|
|
errout = FS.readFile("error_output.txt", {encoding:'utf8'});
|
|
|
|
} catch (e) {
|
|
|
|
//
|
|
|
|
}
|
|
|
|
|
|
|
|
var objout = FS.readFile(objpath, {encoding:'binary'});
|
|
|
|
putWorkFile(objpath, objout);
|
|
|
|
if (!anyTargetChanged(step, [objpath]))
|
|
|
|
return;
|
|
|
|
|
|
|
|
var symbolmap = {};
|
|
|
|
var segments = [];
|
|
|
|
var listings : CodeListingMap = {};
|
|
|
|
lstfiles.forEach((lstfn) => {
|
|
|
|
var lst = FS.readFile(lstfn, {encoding:'utf8'}) as string;
|
|
|
|
lst.split('\n').forEach((line) => {
|
|
|
|
var toks = line.split(/\s*\|\s*/);
|
|
|
|
if (toks && toks[6]) {
|
|
|
|
var toks2 = toks[1].split(/\s+/);
|
|
|
|
var toks3 = toks[6].split(/[:/]/, 4);
|
|
|
|
var path = toks2[1];
|
|
|
|
if (path && toks2[2] && toks3[1]) {
|
|
|
|
var lstline = {
|
|
|
|
line:parseInt(toks2[2]),
|
|
|
|
offset:parseInt(toks3[1].trim(),16),
|
|
|
|
insns:toks3[2],
|
|
|
|
cycles:null,
|
|
|
|
iscode:false // TODO
|
|
|
|
};
|
|
|
|
var lst = listings[path];
|
|
|
|
if (!lst) listings[path] = lst = {lines:[]};
|
|
|
|
lst.lines.push(lstline);
|
|
|
|
//console.log(path,toks2,toks3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return {
|
|
|
|
output:objout, //.slice(0),
|
|
|
|
listings:listings,
|
|
|
|
errors:errors,
|
|
|
|
symbolmap:symbolmap,
|
|
|
|
segments:segments
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-29 00:14:42 +00:00
|
|
|
// README.md:2:5: parse error, expected: statement or variable assignment, integer variable, variable assignment
|
|
|
|
function compileFastBasic(step:BuildStep) {
|
|
|
|
// TODO: fastbasic-fp?
|
|
|
|
loadNative("fastbasic-int");
|
|
|
|
var params = step.params;
|
|
|
|
gatherFiles(step, {mainFilePath:"main.fb"});
|
|
|
|
var destpath = step.prefix + '.s';
|
|
|
|
var errors = [];
|
|
|
|
if (staleFiles(step, [destpath])) {
|
|
|
|
var fastbasic = emglobal.fastbasic({
|
|
|
|
instantiateWasm: moduleInstFn('fastbasic-int'),
|
|
|
|
noInitialRun:true,
|
|
|
|
print:print_fn,
|
|
|
|
printErr:makeErrorMatcher(errors, /(.+?):(\d+):(\d+):\s*(.+)/, 2, 4, step.path, 1),
|
|
|
|
});
|
|
|
|
var FS = fastbasic['FS'];
|
|
|
|
populateFiles(step, FS);
|
|
|
|
params.libargs = ['fastbasic-int.lib'];
|
|
|
|
params.cfgfile = 'fastbasic-cart.cfg';
|
|
|
|
//params.extra_compile_args = ["--asm-define", "NO_SMCODE"];
|
|
|
|
params.extra_link_files = ['fastbasic-int.lib', 'fastbasic-cart.cfg'];
|
|
|
|
//fixParamsWithDefines(step.path, params);
|
|
|
|
var args = [step.path, destpath];
|
|
|
|
execMain(step, fastbasic, args);
|
|
|
|
if (errors.length)
|
|
|
|
return {errors:errors};
|
|
|
|
var asmout = FS.readFile(destpath, {encoding:'utf8'});
|
|
|
|
putWorkFile(destpath, asmout);
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
nexttool:"ca65",
|
|
|
|
path:destpath,
|
|
|
|
args:[destpath],
|
|
|
|
files:[destpath],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-08-05 04:48:29 +00:00
|
|
|
function compileBASIC(step:BuildStep) {
|
|
|
|
var jsonpath = step.path + ".json";
|
|
|
|
gatherFiles(step);
|
|
|
|
if (staleFiles(step, [jsonpath])) {
|
|
|
|
setupRequireFunction();
|
|
|
|
loadGen("common/basic/compiler");
|
|
|
|
var parser = new emglobal['BASICParser']();
|
|
|
|
delete emglobal['require'];
|
|
|
|
var code = getWorkFileAsString(step.path);
|
|
|
|
try {
|
|
|
|
var ast = parser.parseFile(code, step.path);
|
|
|
|
} catch (e) {
|
|
|
|
console.log(e);
|
|
|
|
if (parser.errors.length == 0) throw e;
|
|
|
|
}
|
|
|
|
if (parser.errors.length) {
|
|
|
|
return {errors: parser.errors};
|
|
|
|
}
|
|
|
|
// put AST into JSON (sans source locations) to see if it has changed
|
|
|
|
var json = JSON.stringify(ast, (key,value) => { return (key=='$loc'?undefined:value) });
|
|
|
|
putWorkFile(jsonpath, json);
|
|
|
|
if (anyTargetChanged(step, [jsonpath])) return {
|
|
|
|
output: ast,
|
|
|
|
listings: parser.getListings(),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-28 14:10:59 +00:00
|
|
|
////////////////////////////
|
|
|
|
|
2017-01-15 03:46:12 +00:00
|
|
|
var TOOLS = {
|
2017-01-04 21:07:59 +00:00
|
|
|
'dasm': assembleDASM,
|
2018-06-28 00:26:22 +00:00
|
|
|
//'acme': assembleACME,
|
|
|
|
//'plasm': compilePLASMA,
|
2017-01-06 00:14:12 +00:00
|
|
|
'cc65': compileCC65,
|
2018-06-25 04:52:40 +00:00
|
|
|
'ca65': assembleCA65,
|
|
|
|
'ld65': linkLD65,
|
2018-06-28 00:26:22 +00:00
|
|
|
//'z80asm': assembleZ80ASM,
|
|
|
|
//'sccz80': compileSCCZ80,
|
2018-06-25 04:52:40 +00:00
|
|
|
'sdasz80': assembleSDASZ80,
|
|
|
|
'sdldz80': linkSDLDZ80,
|
2017-01-13 02:21:35 +00:00
|
|
|
'sdcc': compileSDCC,
|
2019-03-21 01:38:53 +00:00
|
|
|
'xasm6809': assembleXASM6809,
|
2019-12-07 23:20:01 +00:00
|
|
|
'cmoc': compileCMOC,
|
|
|
|
'lwasm': assembleLWASM,
|
|
|
|
'lwlink': linkLWLINK,
|
2018-06-25 04:52:40 +00:00
|
|
|
//'naken': assembleNAKEN,
|
2017-11-11 19:45:32 +00:00
|
|
|
'verilator': compileVerilator,
|
2017-11-20 01:32:58 +00:00
|
|
|
'yosys': compileYosys,
|
2018-06-25 04:52:40 +00:00
|
|
|
//'caspr': compileCASPR,
|
2018-06-28 00:26:22 +00:00
|
|
|
'jsasm': compileJSASMStep,
|
2018-08-28 14:10:59 +00:00
|
|
|
'zmac': assembleZMAC,
|
2019-08-16 01:25:08 +00:00
|
|
|
'nesasm': assembleNESASM,
|
2020-06-14 01:28:58 +00:00
|
|
|
'smlrc': compileSmallerC,
|
|
|
|
'yasm': assembleYASM,
|
2018-11-20 17:31:19 +00:00
|
|
|
'bataribasic': compileBatariBasic,
|
2018-11-21 16:53:33 +00:00
|
|
|
'markdown': translateShowdown,
|
2020-07-06 23:53:20 +00:00
|
|
|
'inform6': compileInform6,
|
2020-07-25 23:33:55 +00:00
|
|
|
'merlin32': assembleMerlin32,
|
2020-07-29 00:14:42 +00:00
|
|
|
'fastbasic': compileFastBasic,
|
2020-08-05 04:48:29 +00:00
|
|
|
'basic': compileBASIC,
|
2017-01-04 21:07:59 +00:00
|
|
|
}
|
2016-12-16 01:21:51 +00:00
|
|
|
|
2017-01-23 21:21:45 +00:00
|
|
|
var TOOL_PRELOADFS = {
|
2018-06-20 07:09:37 +00:00
|
|
|
'cc65-apple2': '65-apple2',
|
|
|
|
'ca65-apple2': '65-apple2',
|
|
|
|
'cc65-c64': '65-c64',
|
|
|
|
'ca65-c64': '65-c64',
|
|
|
|
'cc65-nes': '65-nes',
|
|
|
|
'ca65-nes': '65-nes',
|
|
|
|
'cc65-atari8': '65-atari8',
|
|
|
|
'ca65-atari8': '65-atari8',
|
2019-04-24 20:56:53 +00:00
|
|
|
'cc65-vector': '65-sim6502',
|
|
|
|
'ca65-vector': '65-sim6502',
|
2019-08-14 12:56:45 +00:00
|
|
|
'cc65-atari7800': '65-sim6502',
|
|
|
|
'ca65-atari7800': '65-sim6502',
|
2020-10-17 19:34:08 +00:00
|
|
|
'cc65-devel': '65-sim6502',
|
|
|
|
'ca65-devel': '65-sim6502',
|
2017-01-23 21:21:45 +00:00
|
|
|
'sdasz80': 'sdcc',
|
|
|
|
'sdcc': 'sdcc',
|
2018-06-24 05:01:38 +00:00
|
|
|
'sccz80': 'sccz80',
|
2018-11-20 17:31:19 +00:00
|
|
|
'bataribasic': '2600basic',
|
2020-07-06 23:53:20 +00:00
|
|
|
'inform6': 'inform',
|
2020-07-29 00:14:42 +00:00
|
|
|
'fastbasic': '65-atari8',
|
2017-01-23 21:21:45 +00:00
|
|
|
}
|
|
|
|
|
2018-10-05 13:47:15 +00:00
|
|
|
function applyDefaultErrorPath(errors:WorkerError[], path:string) {
|
2018-07-03 03:45:08 +00:00
|
|
|
if (!path) return;
|
|
|
|
for (var i=0; i<errors.length; i++) {
|
|
|
|
var err = errors[i];
|
|
|
|
if (!err.path && err.line) err.path = path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-25 14:43:15 +00:00
|
|
|
function executeBuildSteps() {
|
2018-06-28 03:26:23 +00:00
|
|
|
buildstartseq = workerseq;
|
2019-06-02 00:02:50 +00:00
|
|
|
var linkstep : BuildStep = null;
|
2018-06-25 04:52:40 +00:00
|
|
|
while (buildsteps.length) {
|
|
|
|
var step = buildsteps.shift(); // get top of array
|
|
|
|
var platform = step.platform;
|
|
|
|
var toolfn = TOOLS[step.tool];
|
2019-11-13 20:45:18 +00:00
|
|
|
if (!toolfn) throw Error("no tool named " + step.tool);
|
2019-02-25 21:04:15 +00:00
|
|
|
step.params = PLATFORM_PARAMS[getBasePlatform(platform)];
|
2018-06-25 04:52:40 +00:00
|
|
|
try {
|
|
|
|
step.result = toolfn(step);
|
|
|
|
} catch (e) {
|
2018-06-25 21:51:07 +00:00
|
|
|
console.log("EXCEPTION", e.stack);
|
2018-07-03 03:45:08 +00:00
|
|
|
return {errors:[{line:0, msg:e+""}]}; // TODO: catch errors already generated?
|
2018-06-25 04:52:40 +00:00
|
|
|
}
|
|
|
|
if (step.result) {
|
2018-09-25 23:46:24 +00:00
|
|
|
step.result.params = step.params;
|
2018-06-25 04:52:40 +00:00
|
|
|
// errors? return them
|
2018-09-25 23:46:24 +00:00
|
|
|
if (step.result.errors && step.result.errors.length) {
|
2018-07-03 03:45:08 +00:00
|
|
|
applyDefaultErrorPath(step.result.errors, step.path);
|
2018-06-25 04:52:40 +00:00
|
|
|
return step.result;
|
|
|
|
}
|
|
|
|
// if we got some output, return it immediately
|
|
|
|
if (step.result.output) {
|
|
|
|
return step.result;
|
|
|
|
}
|
|
|
|
// combine files with a link tool?
|
|
|
|
if (step.result.linktool) {
|
2019-06-02 00:02:50 +00:00
|
|
|
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
|
|
|
|
};
|
2018-06-25 04:52:40 +00:00
|
|
|
}
|
2018-06-25 21:51:07 +00:00
|
|
|
}
|
|
|
|
// process with another tool?
|
2018-06-25 04:52:40 +00:00
|
|
|
if (step.result.nexttool) {
|
2019-12-27 22:31:24 +00:00
|
|
|
var asmstep : BuildStep = step.result;
|
2018-11-21 12:21:07 +00:00
|
|
|
asmstep.tool = step.result.nexttool;
|
|
|
|
asmstep.platform = platform;
|
2019-06-02 00:02:50 +00:00
|
|
|
buildsteps.push(asmstep);
|
|
|
|
}
|
|
|
|
// process final step?
|
|
|
|
if (buildsteps.length == 0 && linkstep) {
|
|
|
|
buildsteps.push(linkstep);
|
|
|
|
linkstep = null;
|
2018-06-25 04:52:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-06-25 14:43:15 +00:00
|
|
|
}
|
|
|
|
|
2018-12-05 13:33:40 +00:00
|
|
|
function handleMessage(data : WorkerMessage) : WorkerResult {
|
2018-06-25 14:43:15 +00:00
|
|
|
// preload file system
|
2018-03-03 02:16:25 +00:00
|
|
|
if (data.preload) {
|
|
|
|
var fs = TOOL_PRELOADFS[data.preload];
|
2020-10-17 19:34:08 +00:00
|
|
|
if (!fs && data.platform)
|
|
|
|
fs = TOOL_PRELOADFS[data.preload+'-'+getBasePlatform(data.platform)];
|
2018-06-20 07:09:37 +00:00
|
|
|
if (!fs && data.platform)
|
2019-04-03 21:00:05 +00:00
|
|
|
fs = TOOL_PRELOADFS[data.preload+'-'+getRootBasePlatform(data.platform)];
|
2018-06-20 07:09:37 +00:00
|
|
|
if (fs && !fsMeta[fs])
|
|
|
|
loadFilesystem(fs);
|
2017-01-23 21:21:45 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-06-25 21:51:07 +00:00
|
|
|
// clear filesystem? (TODO: buildkey)
|
|
|
|
if (data.reset) {
|
|
|
|
workfs = {};
|
|
|
|
return;
|
|
|
|
}
|
2018-06-25 14:43:15 +00:00
|
|
|
buildsteps = [];
|
|
|
|
// file updates
|
|
|
|
if (data.updates) {
|
|
|
|
for (var i=0; i<data.updates.length; i++) {
|
|
|
|
var u = data.updates[i];
|
|
|
|
putWorkFile(u.path, u.data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// build steps
|
|
|
|
if (data.buildsteps) {
|
|
|
|
buildsteps.push.apply(buildsteps, data.buildsteps);
|
|
|
|
}
|
|
|
|
// single-file
|
|
|
|
if (data.code) {
|
|
|
|
buildsteps.push(data);
|
|
|
|
}
|
|
|
|
// execute build steps
|
|
|
|
if (buildsteps.length) {
|
|
|
|
var result = executeBuildSteps();
|
|
|
|
return result ? result : {unchanged:true};
|
|
|
|
}
|
2018-06-25 04:52:40 +00:00
|
|
|
// TODO: cache results
|
|
|
|
// message not recognized
|
|
|
|
console.log("Unknown message",data);
|
2018-03-03 02:16:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ENVIRONMENT_IS_WORKER) {
|
|
|
|
onmessage = function(e) {
|
|
|
|
var result = handleMessage(e.data);
|
|
|
|
if (result) {
|
|
|
|
postMessage(result);
|
|
|
|
}
|
2017-01-06 00:14:12 +00:00
|
|
|
}
|
2016-12-16 01:21:51 +00:00
|
|
|
}
|
2019-09-29 18:41:29 +00:00
|
|
|
|
|
|
|
//}();
|