diff --git a/README.md b/README.md index b8c2d3b6..979fbfb0 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ The IDE uses custom forks for many of these, found at https://github.com/sehugg? * https://github.com/wiz-lang/wiz * https://github.com/sylefeb/Silice * https://github.com/steux/cc7800 +* https://bellard.org/tcc/ ### Assemblers/Linkers diff --git a/presets/arm32/crt0.c b/presets/arm32/crt0.c new file mode 100644 index 00000000..a8276119 --- /dev/null +++ b/presets/arm32/crt0.c @@ -0,0 +1,9 @@ + +int main(); + +void _start() { + asm("mov sp, #0x100000"); // stack pointer + main(); + asm(".long 0xdeadc0d3"); +} + diff --git a/presets/arm32/vidfill.c b/presets/arm32/vidfill.c new file mode 100644 index 00000000..9b725230 --- /dev/null +++ b/presets/arm32/vidfill.c @@ -0,0 +1,27 @@ + +//#link "crt0.c" + +const char const str[] = "HELLO WORLD!"; + +int global = 0x1234; +int global2 = 0x123456; + +#define VIDBASE ((int*)0x4000080) + +int vidbuf[160*128]; + +int main() { + *VIDBASE = (int)vidbuf; + global += str[0]; + global++; + global2++; + int c = 0xff880000; + c += str[0]; + int* p = (int*) vidbuf; + for (int i=0; i<160*128; i++) { + p[i] = c++; + } + return 0; +} + + diff --git a/src/common/cpu/ARM.ts b/src/common/cpu/ARM.ts index 13799a60..98da492c 100644 --- a/src/common/cpu/ARM.ts +++ b/src/common/cpu/ARM.ts @@ -27,7 +27,7 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import { Bus, CPU, InstructionBased, SavesState } from "../devices"; +import { Bus, Bus32, CPU, InstructionBased, SavesState } from "../devices"; import { EmuHalt } from "../emu"; import { hex } from "../util"; @@ -103,12 +103,16 @@ export interface ARMCoreState { bankedRegisters: number[][], spsr: number, bankedSPSRs: number[], + sfprs: number[], + dfprs: number[], cycles: number, instructionWidth: 2 | 4 } interface ARMCoreType { gprs: Int32Array; + sfprs: Float32Array; + dfprs: Float64Array; PC: number; SP: number; LR: number; @@ -659,6 +663,11 @@ ARMCoreArm.prototype.constructAddressingMode4Writeback = function(immediate, off } }; +ARMCoreArm.prototype.constructNOP = function() { + this.writesPC = false; + return function() { }; +} + ARMCoreArm.prototype.constructADC = function(rd, rn, shiftOp, condOp) { var cpu : ARMCoreType = this.cpu; var gprs = cpu.gprs; @@ -2658,6 +2667,8 @@ function ARMCore() { this.generateConds(); this.gprs = new Int32Array(16); + this.dfprs = new Float64Array(16); + this.sfprs = new Float32Array(this.dfprs.buffer); // regs shared with dfprs }; ARMCore.prototype.resetCPU = function(startOffset) { @@ -2665,6 +2676,7 @@ ARMCore.prototype.resetCPU = function(startOffset) { this.gprs[i] = 0; } this.gprs[ARMRegs.PC] = startOffset + ARMConstants.WORD_SIZE_ARM; + this.dfprs.set(0); // no need to zero the sfprs, since they share the same buffer this.loadInstruction = this.loadInstructionArm; this.execMode = ARMMode.MODE_ARM; @@ -2769,6 +2781,8 @@ ARMCore.prototype.freeze = function() : ARMCoreState { this.gprs[14], this.gprs[15], ], + 'sfprs': this.sfprs.slice(), + 'dfprs': this.dfprs.slice(), 'mode': this.mode, 'cpsrI': this.cpsrI, 'cpsrF': this.cpsrF, @@ -2850,6 +2864,9 @@ ARMCore.prototype.defrost = function(frost: ARMCoreState) { this.gprs[14] = frost.gprs[14]; this.gprs[15] = frost.gprs[15]; + //this.sfprs.set(frost.sfprs); + this.dfprs.set(frost.dfprs); // regs shared with sfprs + this.mode = frost.mode; this.cpsrI = frost.cpsrI; this.cpsrF = frost.cpsrF; @@ -3667,6 +3684,25 @@ ARMCore.prototype.compileArm = function(instruction) { break; case 0x0C000000: // Coprocessor data transfer + // VSTM, VSTMDB, VSTMIA + if ((instruction & 0x0c100f00) == 0x0c000a00) { + // TODO + op = this.armCompiler.constructNOP(); + /* TODO + const rn = (instruction & 0x000F0000) >> 16; + const vd = (instruction & 0x0000F000) >> 12; + const imm = instruction & 0x000000FF; + const writeback = instruction & 0x00200000; + const increment = instruction & 0x00800000; + const load = instruction & 0x00100000; + const user = instruction & 0x00400000; + op.writesPC = false; + */ + } + else if ((instruction & 0x0c100f00) == 0x0c100a00) { + // TODO: VSTR, VLDR + op = this.armCompiler.constructNOP(); + } break; case 0x0E000000: // Coprocessor data operation/SWI @@ -4103,10 +4139,12 @@ ARMCore.prototype.compileThumb = function(instruction) { /////////////////////////////////////////////////////////////////////////// +type ARMBus = Bus & Bus32; + export class ARM32CPU implements CPU, InstructionBased, ARMMMUInterface, ARMIRQInterface, SavesState { core : ARMCoreType; - bus : Bus; + bus : ARMBus; memory : ARMMemoryRegion[]; BASE_OFFSET = 24; @@ -4122,8 +4160,7 @@ export class ARM32CPU implements CPU, InstructionBased, ARMMMUInterface, ARMIRQI this.memory = []; // TODO for (var i=0; i<256; i++) { // TODO: constant - var bits = 10; - var size = 0x80000; + const bits = 10; this.memory[i] = { PAGE_MASK: (2 << bits) - 1, ICACHE_PAGE_BITS: bits, @@ -4146,12 +4183,13 @@ export class ARM32CPU implements CPU, InstructionBased, ARMMMUInterface, ARMIRQI isStable(): boolean { return true; // TODO? } - connectMemoryBus(bus: Bus): void { + connectMemoryBus(bus: ARMBus): void { this.bus = bus; } reset(): void { this.resetMemory(); - this.core.resetCPU(0); + const resetVector = this.load32(0); + this.core.resetCPU(resetVector); } saveState() : ARMCoreState { return this.core.freeze(); @@ -4173,7 +4211,7 @@ export class ARM32CPU implements CPU, InstructionBased, ARMMMUInterface, ARMIRQI return this.bus.read(a) | (this.bus.read(a+1) << 8); } load32(a: number): number { - var v = this.bus.read(a) | (this.bus.read(a+1) << 8) | (this.bus.read(a+2) << 16) | (this.bus.read(a+3) << 24); + var v = this.bus.read32(a); return v; } // TODO: memory.invalidatePage(maskedOffset); @@ -4185,10 +4223,7 @@ export class ARM32CPU implements CPU, InstructionBased, ARMMMUInterface, ARMIRQI this.bus.write(a+1, (v >> 8) & 0xff); } store32(a: number, v: number): void { - this.bus.write(a, v & 0xff); - this.bus.write(a+1, (v >> 8) & 0xff); - this.bus.write(a+2, (v >> 16) & 0xff); - this.bus.write(a+3, (v >> 24) & 0xff); + this.bus.write32(a, v); } // TODO wait(a: number): void { @@ -4253,4 +4288,10 @@ export class ARM32CPU implements CPU, InstructionBased, ARMMMUInterface, ARMIRQI isThumb() : boolean { return this.core.instructionWidth == 2; } + getDebugTree() { + return { + state: this.saveState(), + mmu: this.core.mmu + }; + } } diff --git a/src/common/devices.ts b/src/common/devices.ts index 0d3933d7..4320205d 100644 --- a/src/common/devices.ts +++ b/src/common/devices.ts @@ -10,6 +10,12 @@ export interface Bus { readConst?(a: number): number; } +export interface Bus32 { + read32(a: number): number; + write32(a: number, v: number): void; + readConst32?(a: number): number; +} + export interface ClockBased { advanceClock(): void; } @@ -276,7 +282,7 @@ export abstract class BasicHeadlessMachine implements HasCPU, Bus, AcceptsROM, P this.probe.logClocks(n); return n; } - probeMemoryBus(membus: Bus): Bus { + probeMemoryBus(membus: Bus & Partial): Bus & Partial { return { read: (a) => { let val = membus.read(a); @@ -286,11 +292,20 @@ export abstract class BasicHeadlessMachine implements HasCPU, Bus, AcceptsROM, P write: (a, v) => { this.probe.logWrite(a, v); membus.write(a, v); + }, + read32: (a) => { + let val = membus.read32(a); + this.probe.logRead(a, val); + return val; + }, + write32: (a, v) => { + this.probe.logWrite(a, v); + membus.write32(a, v); } }; } connectCPUMemoryBus(membus: Bus): void { - this.cpu.connectMemoryBus(this.probeMemoryBus(membus)); + this.cpu.connectMemoryBus(this.probeMemoryBus(membus as Bus&Bus32)); } probeIOBus(iobus: Bus): Bus { return { @@ -302,7 +317,7 @@ export abstract class BasicHeadlessMachine implements HasCPU, Bus, AcceptsROM, P write: (a, v) => { this.probe.logIOWrite(a, v); iobus.write(a, v); - } + }, }; } probeDMABus(iobus: Bus): Bus { diff --git a/src/common/elfparser.ts b/src/common/elfparser.ts new file mode 100644 index 00000000..308cf281 --- /dev/null +++ b/src/common/elfparser.ts @@ -0,0 +1,159 @@ + +export class ELFParser { + readonly dataView: DataView; + readonly sectionHeaders: ElfSectionHeader[]; + readonly symbolTable: ElfSymbolTableEntry[]; + entry: number; + + constructor(data: Uint8Array) { + this.dataView = new DataView(data.buffer); + this.sectionHeaders = []; + this.symbolTable = []; + this.parse(); + } + + private parse() { + const elfHeader = new DataView(this.dataView.buffer, 0, 52); + // check magic # + const magic = elfHeader.getInt32(0, true); + if (magic !== 0x464c457f) { + throw new Error('Invalid ELF header'); + } + // only 32 bit supported + if (elfHeader.getUint8(4) !== 1) { + throw new Error('Only 32-bit ELF supported'); + } + // check version = 1 + if (elfHeader.getUint8(6) !== 1) { + throw new Error('Invalid ELF version'); + } + // get endianness + const endian = elfHeader.getUint8(5) === 1; + if (!endian) { + throw new Error('Big endian not supported'); + } + // get entryPoint + this.entry = elfHeader.getUint32(24, endian); + + // Parse ELF header and extract section header offset + const sectionHeaderOffset = this.dataView.getUint32(32, endian); + // get section header size + const sectionHeaderSize = this.dataView.getUint16(46, endian); + // get # of section headers + const sectionHeaderCount = this.dataView.getUint16(48, endian); + // get index of section with names + const sectionNameIndex = this.dataView.getUint16(50, endian); + + // Parse section headers + for (let i = 0; i < sectionHeaderCount; i++) { + const offset = sectionHeaderOffset + i * sectionHeaderSize; // Each section header is 40 bytes + //const sectionView = new DataView(this.dataView.buffer, offset, sectionHeaderSize); + const section = new ElfSectionHeader(this.dataView, offset); + this.sectionHeaders.push(section); + } + const sectionNameSection = this.sectionHeaders[sectionNameIndex]; + if (!sectionNameSection) { + throw new Error('Invalid ELF section name table'); + } else { + const stringTableOffset = sectionNameSection.offset; + const stringTableSize = sectionNameSection.size; + const sectionNameView = new DataView(this.dataView.buffer, stringTableOffset, stringTableSize); + for (let i = 0; i < sectionHeaderCount; i++) { + this.sectionHeaders[i].stringView = sectionNameView; + } + } + + // Extract the string table + const stringTableSection = this.sectionHeaders.find( + (section) => section.type === ElfSectionType.STRTAB && section.name == '.strtab' + ); + if (stringTableSection) { + const stringTableOffset = stringTableSection.offset; + const stringTableSize = stringTableSection.size; + const stringView = new DataView(this.dataView.buffer, stringTableOffset, stringTableSize); + // Find the symbol table section and string table section + const symbolTableSection = this.sectionHeaders.find( + (section) => section.name === '.symtab' + ); + if (symbolTableSection) { + // Extract the symbol table + const symbolTableOffset = symbolTableSection.offset; + const symbolTableSize = symbolTableSection.size; + const symbolTableEntryCount = symbolTableSize / 16; + //const symbolTable = new DataView(this.dataView.buffer, symbolTableOffset, symbolTableSize); + for (let i = 0; i < symbolTableEntryCount; i++) { + const offset = symbolTableOffset + i * 16; + const entry = new ElfSymbolTableEntry(this.dataView, offset, stringView); + this.symbolTable.push(entry); + } + } + } + } + + getSymbols(): ElfSymbolTableEntry[] { + return this.symbolTable; + } +} + +enum ElfSectionType { + SYMTAB = 2, + STRTAB = 3, +} + +class ElfSectionHeader { + readonly type: number; + stringView: DataView | null = null; + + constructor(readonly dataView: DataView, readonly headerOffset: number) { + this.type = this.dataView.getUint32(this.headerOffset + 0x4, true); + } + get offset(): number { + return this.dataView.getUint32(this.headerOffset + 0x10, true); + } + get size(): number { + return this.dataView.getUint32(this.headerOffset + 0x14, true); + } + get nameOffset(): number { + return this.dataView.getUint32(this.headerOffset + 0x0, true); + } + get name(): string { + return getUTF8(this.stringView!, this.nameOffset); + } + get vaddr(): number { + return this.dataView.getUint32(this.headerOffset + 0xc, true); + } +} + +class ElfSymbolTableEntry { + constructor(readonly dataView: DataView, + readonly entryOffset: number, + readonly stringView: DataView) { + } + + get nameOffset(): number { + return this.dataView.getUint32(this.entryOffset, true); + } + get name(): string { + return getUTF8(this.stringView, this.nameOffset); + } + get value(): number { + return this.dataView.getUint32(this.entryOffset + 4, true); + } + get size(): number { + return this.dataView.getUint32(this.entryOffset + 8, true); + } + get info(): number { + return this.dataView.getUint8(this.entryOffset + 12); + } + get other(): number { + return this.dataView.getUint8(this.entryOffset + 13); + } +} + +function getUTF8(view: DataView, offset: number): string { + let str = ''; + for (let i = offset; view.getUint8(i) !== 0; i++) { + str += String.fromCharCode(view.getUint8(i)); + } + return str; +} \ No newline at end of file diff --git a/src/common/emu.ts b/src/common/emu.ts index 2d190de4..2941e987 100644 --- a/src/common/emu.ts +++ b/src/common/emu.ts @@ -658,7 +658,7 @@ export function padBytes(data:Uint8Array|number[], len:number, padstart?:boolean type AddressReadWriteFn = ((a:number) => number) | ((a:number,v:number) => void); type AddressDecoderEntry = [number, number, number, AddressReadWriteFn]; -type AddressDecoderOptions = {gmask?:number}; +type AddressDecoderOptions = {gmask?:number, defaultval?:number}; // TODO: better performance, check values export function AddressDecoder(table : AddressDecoderEntry[], options?:AddressDecoderOptions) { @@ -679,7 +679,7 @@ export function AddressDecoder(table : AddressDecoderEntry[], options?:AddressDe if (mask) s += "a&="+mask+";"; s += "return this.__fn"+i+"(a,v)&0xff;}\n"; } - s += "return 0;"; // TODO: noise()? + s += "return "+(options?.defaultval|0)+";"; return new Function('a', 'v', s); } return makeFunction().bind(self); diff --git a/src/ide/project.ts b/src/ide/project.ts index 79eefbbb..c40c1376 100644 --- a/src/ide/project.ts +++ b/src/ide/project.ts @@ -274,7 +274,8 @@ export class CodeProject { var depfiles = []; msg.updates.push({path:mainfilename, data:maintext}); this.filename2path[mainfilename] = this.mainPath; - let usesRemoteTool = this.getToolForFilename(mainfilename).startsWith('remote:'); + const tool = this.getToolForFilename(this.mainPath); + let usesRemoteTool = tool.startsWith('remote:'); for (var dep of depends) { // remote tools send both includes and linked files in one build step if (!dep.link || usesRemoteTool) { diff --git a/src/ide/ui.ts b/src/ide/ui.ts index 357b85e8..cc943fa4 100644 --- a/src/ide/ui.ts +++ b/src/ide/ui.ts @@ -136,6 +136,7 @@ const TOOL_TO_SOURCE_STYLE = { 'ecs': 'ecs', 'remote:llvm-mos': 'text/x-csrc', 'cc7800': 'text/x-csrc', + 'armtcc': 'text/x-csrc', } // TODO: move into tool class diff --git a/src/machine/arm32.ts b/src/machine/arm32.ts index e85edc4a..f036c25b 100644 --- a/src/machine/arm32.ts +++ b/src/machine/arm32.ts @@ -1,6 +1,6 @@ import { ARM32CPU, ARMCoreState } from "../common/cpu/ARM"; -import { BasicScanlineMachine, HasSerialIO, SerialEvent, SerialIOInterface } from "../common/devices"; +import { BasicScanlineMachine, Bus32, HasSerialIO, SerialEvent, SerialIOInterface } from "../common/devices"; import { newAddressDecoder, Keys, makeKeycodeMap, newKeyboardHandler, EmuHalt } from "../common/emu"; import { Debuggable, EmuState } from "../common/baseplatform"; import { hex, lpad } from "../common/util"; @@ -18,36 +18,42 @@ var GBA_KEYCODE_MAP = makeKeycodeMap([ [Keys.DOWN, 0, 0x80], ]); -const ROM_START = 0x0; -const ROM_SIZE = 0x80000; -const RAM_START = 0x2000000; -const RAM_SIZE = 0x80000; +const RAM_START = 0x0; +const RAM_SIZE = 0x100000; +const ROM_BASE = 0x10000; +const ENTRY_POINT = 0x20000; const IO_START = 0x4000000; const IO_SIZE = 0x100; const MAX_SERIAL_CHARS = 1000000; const CPU_FREQ = 4000000; // 4 MHz -export class ARM32Machine extends BasicScanlineMachine implements Debuggable, HasSerialIO { +const ILLEGAL_OPCODE = 0xedededed; + +export class ARM32Machine extends BasicScanlineMachine + implements Debuggable, HasSerialIO, Bus32 { cpuFrequency = CPU_FREQ; // MHz canvasWidth = 160; numTotalScanlines = 256; numVisibleScanlines = 128; cpuCyclesPerLine = Math.floor(CPU_FREQ / (256*60)); - defaultROMSize = 512*1024; + defaultROMSize = RAM_SIZE - ROM_BASE; sampleRate = 1; cpu: ARM32CPU = new ARM32CPU(); - ram = new Uint8Array(96*1024); + ram = new Uint8Array(RAM_SIZE); ram16 = new Uint16Array(this.ram.buffer); + ram32 = new Uint32Array(this.ram.buffer); pixels32 : Uint32Array; pixels8 : Uint8Array; - vidbase : number = 0; + rombase : number = ROM_BASE; brightness : number = 255; serial : SerialIOInterface; serialOut : SerialEvent[]; serialIn : SerialEvent[]; + ioregs = new Uint8Array(IO_SIZE); + ioregs32 = new Uint32Array(this.ioregs.buffer); constructor() { super(); @@ -65,7 +71,25 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha this.serial = serial; } + loadROM(rom: Uint8Array) { + super.loadROM(rom); + } + reset() { + if (this.rom) { + this.ram.set(this.rom, this.rombase); + // set ARM vectors + const obj32 = new Uint32Array(this.ram.buffer); + const start = ENTRY_POINT; + obj32[0] = start; // set reset vector + obj32[1] = start; // set undefined vector + obj32[2] = start; // set swi vector + obj32[3] = start; // set prefetch abort vector + obj32[4] = start; // set data abort vector + obj32[5] = start; // set reserved vector + obj32[6] = start; // set irq vector + obj32[7] = start; // set fiq vector + } super.reset(); this.serialOut = []; this.serialIn = []; @@ -74,19 +98,13 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha // TODO: 32-bit bus? read = newAddressDecoder([ - [ROM_START, ROM_START+ROM_SIZE-1, ROM_SIZE-1, (a) => { - return this.rom ? this.rom[a] : 0; - }], [RAM_START, RAM_START+RAM_SIZE-1, RAM_SIZE-1, (a) => { return this.ram[a]; }], [IO_START, IO_START+IO_SIZE-1, IO_SIZE-1, (a, v) => { return this.readIO(a); }], - [0, (1<<31)-1, 0, (a, v) => { - throw new EmuHalt(`Address read out of bounds: 0x${hex(a)}`); - }] - ]); + ], {defaultval: ILLEGAL_OPCODE & 0xff}); write = newAddressDecoder([ [RAM_START, RAM_START+RAM_SIZE-1, RAM_SIZE-1, (a, v) => { @@ -97,6 +115,30 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha }], ]); + read32 = (a) => { + if (a >= RAM_START && a < RAM_SIZE && (a & 3) == 0) { + return this.ram32[a >> 2]; + } else { + return this.read(a) | (this.read(a+1)<<8) | (this.read(a+2)<<16) | (this.read(a+3)<<24); + } + }; + + write32 = (a, v) => { + if (a >= RAM_START && a < RAM_SIZE && (a & 3) == 0) { + this.ram32[a >> 2] = v; + } else { + this.write(a, v & 0xff); + this.write(a+1, (v>>8) & 0xff); + this.write(a+2, (v>>16) & 0xff); + this.write(a+3, (v>>24) & 0xff); + } + } + + readAddress(a : number) : number { + if (a >= RAM_START && a < RAM_START+RAM_SIZE) return this.read(a); + else return ILLEGAL_OPCODE; + } + readIO(a : number) : number { switch (a) { case 0x0: @@ -116,6 +158,7 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha } writeIO(a : number, v : number) : void { + this.ioregs[a] = v; switch (a) { case 0x0: //this.brightness = v & 0xff; @@ -136,7 +179,8 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha postFrame() { var p32 = this.pixels32; - var vbase = (this.vidbase >> 1) & 0xfffff; + const vidbase = this.ioregs32[0x80 >> 2]; + var vbase = (vidbase >> 1) & 0xfffff; var mask = this.brightness << 24; for (var i=0; i>2],8); + if (sp == fp) s += ' FP'; + s += '\n'; + sp += 4; + if (sp >= RAM_START+RAM_SIZE) break; + } + return s; case 'CPU': var s = ''; var c = state.c as ARMCoreState; const EXEC_MODE = {2:'Thumb',4:'ARM'}; const REGNAMES = {15:'PC',14:'LR',13:'SP',12:'IP',11:'FP',9:'SB'}; - for (var i=0; i<16; i++) { - s += lpad(REGNAMES[i]||'',3) + lpad('r'+i, 5) + ' ' + hex(c.gprs[i],8) + '\n'; + for (var i=0; i<8; i++) { + let j = i+8; + s += lpad('r'+i, 5) + ' ' + hex(c.gprs[i],8) + ' '; + s += lpad('r'+j, 5) + ' ' + hex(c.gprs[j],8) + lpad(REGNAMES[j]||'',3) + '\n'; } s += 'Flags '; s += c.cpsrN ? " N" : " -"; diff --git a/src/platform/arm32.ts b/src/platform/arm32.ts index 54dbc2d6..5f5e637d 100644 --- a/src/platform/arm32.ts +++ b/src/platform/arm32.ts @@ -1,40 +1,28 @@ -import { BaseDebugPlatform, CpuState, EmuState, Platform, DisasmLine, Debuggable, Machine, BaseMachinePlatform } from "../common/baseplatform"; -import { AnimationTimer, EmuHalt, padBytes, PLATFORMS, RasterVideo } from "../common/emu"; -import { hex, lpad, loadScript } from "../common/util"; -import { ARM32CPU } from "../common/cpu/ARM"; +import { Platform, DisasmLine, Machine, BaseMachinePlatform } from "../common/baseplatform"; +import { PLATFORMS } from "../common/emu"; +import { loadScript } from "../common/util"; import { ARM32Machine } from "../machine/arm32"; -declare var uc, cs : any; // Unicorn module +declare var cs : any; // Unicorn module const ARM32_PRESETS = [ - { id: 'vidfill.vasm', name: 'Video Memory Fill' }, + { id: 'vidfill.c', name: 'Video Memory Fill' }, ]; -const SCREEN_WIDTH = 160; -const SCREEN_HEIGHT = 128; -const ROM_START_ADDR = 0x0; -const HIROM_START_ADDR = 0xff800000; -const ROM_SIZE = 512*1024; -const RAM_START_ADDR = 0x20000000; -const RAM_SIZE = 512*1024; -const CLOCKS_PER_FRAME = 10000; - -interface ARM32State extends EmuState { - r: Uint32Array; // registers -} - export abstract class BaseARMMachinePlatform extends BaseMachinePlatform { //getOpcodeMetadata = getOpcodeMetadata_z80; getToolForFilename(fn: string) { + fn = fn.toLowerCase(); if (fn.endsWith('.vasm')) return "vasmarm"; - else if (fn.endsWith('.armips')) return "armips"; - else return "vasmarm"; + if (fn.endsWith('.armips')) return "armips"; + if (fn.endsWith('.c')) return "armtcc"; + if (fn.endsWith('.s')) return "armtcc"; + return "armtcc"; } getPresets() { return ARM32_PRESETS; } - getDefaultExtension() { return ".vasm"; }; - + getDefaultExtension() { return ".c"; }; } class ARM32Platform extends BaseARMMachinePlatform implements Platform { @@ -53,10 +41,10 @@ class ARM32Platform extends BaseARMMachinePlatform implements Plat newMachine() { return new ARM32Machine(); } readAddress(a) { return this.machine.read(a); } getMemoryMap = function() { return { main:[ - {name:'ROM',start:0x0000000,size:0x80000,type:'rom'}, - {name:'RAM',start:0x2000000,size:0x80000,type:'ram'}, + {name:'ROM',start:0x0000000,size:0x100000,type:'ram'}, {name:'I/O',start:0x4000000,size:0x100,type:'io'}, ] } }; + getDebugTree() { return this.machine.cpu.getDebugTree(); } disassemble(pc:number, read:(addr:number)=>number) : DisasmLine { var is_thumb = this.machine.cpu.isThumb(); var capstone = is_thumb ? this.capstone_thumb : this.capstone_arm; diff --git a/src/worker/lib/arm32/arm-libtcc1.a b/src/worker/lib/arm32/arm-libtcc1.a new file mode 100644 index 00000000..8817120e Binary files /dev/null and b/src/worker/lib/arm32/arm-libtcc1.a differ diff --git a/src/worker/lib/arm32/arm-tcc.o b/src/worker/lib/arm32/arm-tcc.o new file mode 100644 index 00000000..e967251d Binary files /dev/null and b/src/worker/lib/arm32/arm-tcc.o differ diff --git a/src/worker/platforms.ts b/src/worker/platforms.ts index 52e2f51a..f945f684 100644 --- a/src/worker/platforms.ts +++ b/src/worker/platforms.ts @@ -334,6 +334,11 @@ export var PLATFORM_PARAMS = { extra_link_files: ['crt0.o', 'exidy.cfg'], //extra_compile_files: ['exidy.h'], }, + 'arm32': { + arch: 'arm32', + define: ['__ARM__'], + extra_link_files: ['arm-tcc.o', 'arm-libtcc1.a'], + }, }; PLATFORM_PARAMS['sms-sms-libcv'] = PLATFORM_PARAMS['sms-sg1000-libcv']; diff --git a/src/worker/tools/arm.ts b/src/worker/tools/arm.ts index c59fcc9d..5ae12296 100644 --- a/src/worker/tools/arm.ts +++ b/src/worker/tools/arm.ts @@ -1,7 +1,7 @@ import { hex } from "../../common/util"; import { CodeListingMap, SourceLine, WorkerError, WorkerResult } from "../../common/workertypes"; -import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, putWorkFile, anyTargetChanged, getPrefix, getWorkFileAsString } from "../builder"; +import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, putWorkFile, anyTargetChanged, getPrefix, getWorkFileAsString, populateExtraFiles } from "../builder"; import { makeErrorMatcher, re_crlf } from "../listingutils"; import { loadNative, moduleInstFn, execMain, emglobal, EmscriptenModule } from "../wasmutils"; @@ -239,3 +239,109 @@ export function assembleVASMARM(step: BuildStep): BuildStepResult { } } +function tccErrorMatcher(errors: WorkerError[], mainpath: string) { + return makeErrorMatcher(errors, /(\w+|tcc):(\d+|\s*error): (.+)/, 2, 3, mainpath, 1);; +} + +export async function compileARMTCC(step: BuildStep): Promise { + loadNative("arm-tcc"); + const params = step.params; + const errors = []; + gatherFiles(step, { mainFilePath: "main.c" }); + const objpath = step.prefix + ".o"; + const error_fn = tccErrorMatcher(errors, step.path); + + if (staleFiles(step, [objpath])) { + const armtcc: EmscriptenModule = await emglobal.armtcc({ + instantiateWasm: moduleInstFn('arm-tcc'), + noInitialRun: true, + print: error_fn, + printErr: error_fn, + }); + + var args = ['-I.', '-c', + //'-std=c11', + '-funsigned-char', + '-Wwrite-strings', + '-gdwarf', + '-o', objpath]; + if (params.define) { + params.define.forEach((x) => args.push('-D' + x)); + } + args.push(step.path); + + const FS = armtcc.FS; + populateExtraFiles(step, FS, params.extra_compile_files); + populateFiles(step, FS); + execMain(step, armtcc, args); + if (errors.length) + return { errors: errors }; + + var objout = FS.readFile(objpath, { encoding: 'binary' }) as Uint8Array; + putWorkFile(objpath, objout); + } + return { + linktool: "armtcclink", + files: [objpath], + args: [objpath] + } +} + +export async function linkARMTCC(step: BuildStep): Promise { + loadNative("arm-tcc"); + const params = step.params; + const errors = []; + gatherFiles(step, { mainFilePath: "main.c" }); + const objpath = "main.bin"; + const error_fn = tccErrorMatcher(errors, step.path); + + if (staleFiles(step, [objpath])) { + const armtcc: EmscriptenModule = await emglobal.armtcc({ + instantiateWasm: moduleInstFn('arm-tcc'), + noInitialRun: true, + print: error_fn, + printErr: error_fn, + }); + + var args = ['-L.', '-nostdlib', '-nostdinc', + '-Wl,--oformat=binary', + //'-Wl,-section-alignment=0x100000', + '-gdwarf', + '-o', objpath]; + if (params.define) { + params.define.forEach((x) => args.push('-D' + x)); + } + let objfiles = step.files; + // sort so that crtxxx files are first + objfiles.sort((a, b) => { + let a0 = a.startsWith('crt') ? 0 : 1; + let b0 = b.startsWith('crt') ? 0 : 1; + a = a0 + a; + b = b0 + b; + return a.localeCompare(b); + }); + args = args.concat(objfiles); + args.push('arm-libtcc1.a'); + + const FS = armtcc.FS; + populateExtraFiles(step, FS, params.extra_link_files); + populateFiles(step, FS); + execMain(step, armtcc, args); + if (errors.length) + return { errors: errors }; + + var objout = FS.readFile(objpath, { encoding: 'binary' }) as Uint8Array; + putWorkFile(objpath, objout); + if (!anyTargetChanged(step, [objpath])) + return; + + return { + output: objout, //.slice(0x34), + //listings: listings, + errors: errors, + //symbolmap: symbolmap, + //segments: segments + }; + } +} + diff --git a/src/worker/wasm/arm-tcc.js b/src/worker/wasm/arm-tcc.js new file mode 100644 index 00000000..3e7f5395 --- /dev/null +++ b/src/worker/wasm/arm-tcc.js @@ -0,0 +1,21 @@ + +var armtcc = (() => { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; + if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename; + return ( +function(armtcc = {}) { + +var Module=typeof armtcc!="undefined"?armtcc:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;if(ENVIRONMENT_IS_NODE){var fs=require("fs");var nodePath=require("path");if(ENVIRONMENT_IS_WORKER){scriptDirectory=nodePath.dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=(filename,binary)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);return fs.readFileSync(filename,binary?undefined:"utf8")};readBinary=filename=>{var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret};readAsync=(filename,onload,onerror,binary=true)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);fs.readFile(filename,binary?undefined:"utf8",(err,data)=>{if(err)onerror(err);else onload(binary?data.buffer:data)})};if(!Module["thisProgram"]&&process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow};Module["inspect"]=()=>"[Emscripten Module object]"}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=(url,onload,onerror)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=title=>document.title=title}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort(text)}}var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeKeepaliveCounter=0;function keepRuntimeAlive(){return noExitRuntime||runtimeKeepaliveCounter>0}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();FS.ignorePermissions=false;TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return filename.startsWith(dataURIPrefix)}function isFileURI(filename){return filename.startsWith("file://")}var wasmBinaryFile;wasmBinaryFile="arm-tcc.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}catch(err){abort(err)}}function getBinaryPromise(binaryFile){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch=="function"&&!isFileURI(binaryFile)){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{if(!response["ok"]){throw"failed to load wasm binary file at '"+binaryFile+"'"}return response["arrayBuffer"]()}).catch(()=>getBinary(binaryFile))}else{if(readAsync){return new Promise((resolve,reject)=>{readAsync(binaryFile,response=>resolve(new Uint8Array(response)),reject)})}}}return Promise.resolve().then(()=>getBinary(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>{return WebAssembly.instantiate(binary,imports)}).then(instance=>{return instance}).then(receiver,reason=>{err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}else{return instantiateArrayBuffer(binaryFile,imports,callback)}}function createWasm(){var info={"a":wasmImports};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["F"];updateMemoryViews();wasmTable=Module["asm"]["K"];addOnInit(Module["asm"]["G"]);removeRunDependency("wasm-instantiate");return exports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err("Module.instantiateWasm callback failed with error: "+e);readyPromiseReject(e)}}instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}var tempDouble;var tempI64;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}function callRuntimeCallbacks(callbacks){while(callbacks.length>0){callbacks.shift()(Module)}}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:path=>{if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},join:function(){var paths=Array.prototype.slice.call(arguments);return PATH.normalize(paths.join("/"))},join2:(l,r)=>{return PATH.normalize(l+"/"+r)}};function initRandomFill(){if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){return view=>crypto.getRandomValues(view)}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");var randomFillSync=crypto_module["randomFillSync"];if(randomFillSync){return view=>crypto_module["randomFillSync"](view)}var randomBytes=crypto_module["randomBytes"];return view=>(view.set(randomBytes(view.byteLength)),view)}catch(e){}}abort("initRandomDevice")}function randomFill(view){return(randomFill=initRandomFill())(view)}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heapOrArray,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str}var TTY={ttys:[],init:function(){},shutdown:function(){},register:function(dev,ops){TTY.ttys[dev]={input:[],output:[],ops:ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open:function(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close:function(stream){stream.tty.ops.fsync(stream.tty)},fsync:function(stream){stream.tty.ops.fsync(stream.tty)},read:function(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function mmapAlloc(size){abort()}var MEMFS={ops_table:null,mount:function(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode:function(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}if(!MEMFS.ops_table){MEMFS.ops_table={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}}}var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp}return node},getFileDataAsTypedArray:function(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage:function(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr:function(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr:function(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup:function(parent,name){throw FS.genericErrors[44]},mknod:function(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename:function(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp;old_node.parent=new_dir},unlink:function(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir:function(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir:function(node){var entries=[".",".."];for(var key in node.contents){if(!node.contents.hasOwnProperty(key)){continue}entries.push(key)}return entries},symlink:function(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink:function(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read:function(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{assert(arrayBuffer,`Loading data file "${url}" failed (no arrayBuffer).`);onload(new Uint8Array(arrayBuffer));if(dep)removeRunDependency(dep)},event=>{if(onerror){onerror()}else{throw`Loading data file "${url}" failed.`}});if(dep)addRunDependency(dep)}var preloadPlugins=Module["preloadPlugins"]||[];function FS_handledByPreloadPlugin(byteArray,fullname,finish,onerror){if(typeof Browser!="undefined")Browser.init();var handled=false;preloadPlugins.forEach(function(plugin){if(handled)return;if(plugin["canHandle"](fullname)){plugin["handle"](byteArray,fullname,finish,onerror);handled=true}});return handled}function FS_createPreloadedFile(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish){var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency("cp "+fullname);function processData(byteArray){function finish(byteArray){if(preFinish)preFinish();if(!dontCreateFile){FS.createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}if(onload)onload();removeRunDependency(dep)}if(FS_handledByPreloadPlugin(byteArray,fullname,finish,()=>{if(onerror)onerror();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url,byteArray=>processData(byteArray),onerror)}else{processData(url)}}function FS_modeStringToFlags(str){var flagModes={"r":0,"r+":2,"w":512|64|1,"w+":512|64|2,"a":1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error("Unknown file open mode: "+str)}return flags}function FS_getMode(canRead,canWrite){var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode}var WORKERFS={DIR_MODE:16895,FILE_MODE:33279,reader:null,mount:function(mount){assert(ENVIRONMENT_IS_WORKER);if(!WORKERFS.reader)WORKERFS.reader=new FileReaderSync;var root=WORKERFS.createNode(null,"/",WORKERFS.DIR_MODE,0);var createdParents={};function ensureParent(path){var parts=path.split("/");var parent=root;for(var i=0;i=stream.node.size)return 0;var chunk=stream.node.contents.slice(position,position+length);var ab=WORKERFS.reader.readAsArrayBuffer(chunk);buffer.set(new Uint8Array(ab),offset);return chunk.size},write:function(stream,buffer,offset,length,position){throw new FS.ErrnoError(29)},llseek:function(stream,offset,whence){var position=offset;if(whence===1){position+=stream.position}else if(whence===2){if(FS.isFile(stream.node.mode)){position+=stream.node.size}}if(position<0){throw new FS.ErrnoError(28)}return position}}};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,ErrnoError:null,genericErrors:{},filesystems:null,syncFSRequests:0,lookupPath:(path,opts={})=>{path=PATH_FS.resolve(path);if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};opts=Object.assign(defaults,opts);if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:node=>{var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:(parentid,name)=>{var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:node=>{var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:node=>{var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:(parent,name)=>{var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:(parent,name,mode,rdev)=>{var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:node=>{FS.hashRemoveNode(node)},isRoot:node=>{return node===node.parent},isMountpoint:node=>{return!!node.mounted},isFile:mode=>{return(mode&61440)===32768},isDir:mode=>{return(mode&61440)===16384},isLink:mode=>{return(mode&61440)===40960},isChrdev:mode=>{return(mode&61440)===8192},isBlkdev:mode=>{return(mode&61440)===24576},isFIFO:mode=>{return(mode&61440)===4096},isSocket:mode=>{return(mode&49152)===49152},flagsToPermissionString:flag=>{var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:(node,perms)=>{if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup:dir=>{var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:(dir,name)=>{try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:(dir,name,isdir)=>{var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:(node,flags)=>{if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:(fd_start=0,fd_end=FS.MAX_OPEN_FDS)=>{for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:fd=>FS.streams[fd],createStream:(stream,fd_start,fd_end)=>{if(!FS.FSStream){FS.FSStream=function(){this.shared={}};FS.FSStream.prototype={};Object.defineProperties(FS.FSStream.prototype,{object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}},flags:{get:function(){return this.shared.flags},set:function(val){this.shared.flags=val}},position:{get:function(){return this.shared.position},set:function(val){this.shared.position=val}}})}stream=Object.assign(new FS.FSStream,stream);var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:fd=>{FS.streams[fd]=null},chrdev_stream_ops:{open:stream=>{var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:()=>{throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice:(dev,ops)=>{FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts:mount=>{var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:(populate,callback)=>{if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:(type,opts,mountpoint)=>{var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:mountpoint=>{var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:(parent,name)=>{return parent.node_ops.lookup(parent,name)},mknod:(path,mode,dev)=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:(path,mode)=>{mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:(path,mode)=>{mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:(path,mode)=>{var dirs=path.split("/");var d="";for(var i=0;i{if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink:(oldpath,newpath)=>{if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename:(old_path,new_path)=>{var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name)}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir:path=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir:path=>{var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node.node_ops.readdir){throw new FS.ErrnoError(54)}return node.node_ops.readdir(node)},unlink:path=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink:path=>{var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return PATH_FS.resolve(FS.getPath(link.parent),link.node_ops.readlink(link))},stat:(path,dontFollow)=>{var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;if(!node){throw new FS.ErrnoError(44)}if(!node.node_ops.getattr){throw new FS.ErrnoError(63)}return node.node_ops.getattr(node)},lstat:path=>{return FS.stat(path,true)},chmod:(path,mode,dontFollow)=>{var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{mode:mode&4095|node.mode&~4095,timestamp:Date.now()})},lchmod:(path,mode)=>{FS.chmod(path,mode,true)},fchmod:(fd,mode)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chmod(stream.node,mode)},chown:(path,uid,gid,dontFollow)=>{var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{timestamp:Date.now()})},lchown:(path,uid,gid)=>{FS.chown(path,uid,gid,true)},fchown:(fd,uid,gid)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chown(stream.node,uid,gid)},truncate:(path,len)=>{if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}node.node_ops.setattr(node,{size:len,timestamp:Date.now()})},ftruncate:(fd,len)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.truncate(stream.node,len)},utime:(path,atime,mtime)=>{var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;node.node_ops.setattr(node,{timestamp:Math.max(atime,mtime)})},open:(path,flags,mode)=>{if(path===""){throw new FS.ErrnoError(44)}flags=typeof flags=="string"?FS_modeStringToFlags(flags):flags;mode=typeof mode=="undefined"?438:mode;if(flags&64){mode=mode&4095|32768}else{mode=0}var node;if(typeof path=="object"){node=path}else{path=PATH.normalize(path);try{var lookup=FS.lookupPath(path,{follow:!(flags&131072)});node=lookup.node}catch(e){}}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else{node=FS.mknod(path,mode,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node:node,path:FS.getPath(node),flags:flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(Module["logReadFiles"]&&!(flags&1)){if(!FS.readFiles)FS.readFiles={};if(!(path in FS.readFiles)){FS.readFiles[path]=1}}return stream},close:stream=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed:stream=>{return stream.fd===null},llseek:(stream,offset,whence)=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read:(stream,buffer,offset,length,position)=>{if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write:(stream,buffer,offset,length,position,canOwn)=>{if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},allocate:(stream,offset,length)=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(offset<0||length<=0){throw new FS.ErrnoError(28)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(!FS.isFile(stream.node.mode)&&!FS.isDir(stream.node.mode)){throw new FS.ErrnoError(43)}if(!stream.stream_ops.allocate){throw new FS.ErrnoError(138)}stream.stream_ops.allocate(stream,offset,length)},mmap:(stream,length,position,prot,flags)=>{if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync:(stream,buffer,offset,length,mmapFlags)=>{if(!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},munmap:stream=>0,ioctl:(stream,cmd,arg)=>{if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile:(path,opts={})=>{opts.flags=opts.flags||0;opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){throw new Error('Invalid encoding type "'+opts.encoding+'"')}var ret;var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){ret=UTF8ArrayToString(buf,0)}else if(opts.encoding==="binary"){ret=buf}FS.close(stream);return ret},writeFile:(path,data,opts={})=>{opts.flags=opts.flags||577;var stream=FS.open(path,opts.flags,opts.mode);if(typeof data=="string"){var buf=new Uint8Array(lengthBytesUTF8(data)+1);var actualNumBytes=stringToUTF8Array(data,buf,0,buf.length);FS.write(stream,buf,0,actualNumBytes,undefined,opts.canOwn)}else if(ArrayBuffer.isView(data)){FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn)}else{throw new Error("Unsupported data type")}FS.close(stream)},cwd:()=>FS.currentPath,chdir:path=>{var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories:()=>{FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices:()=>{FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomLeft=randomFill(randomBuffer).byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories:()=>{FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount:()=>{var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup:(parent,name)=>{var fd=+name;var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams:()=>{if(Module["stdin"]){FS.createDevice("/dev","stdin",Module["stdin"])}else{FS.symlink("/dev/tty","/dev/stdin")}if(Module["stdout"]){FS.createDevice("/dev","stdout",null,Module["stdout"])}else{FS.symlink("/dev/tty","/dev/stdout")}if(Module["stderr"]){FS.createDevice("/dev","stderr",null,Module["stderr"])}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},ensureErrnoError:()=>{if(FS.ErrnoError)return;FS.ErrnoError=function ErrnoError(errno,node){this.name="ErrnoError";this.node=node;this.setErrno=function(errno){this.errno=errno};this.setErrno(errno);this.message="FS error"};FS.ErrnoError.prototype=new Error;FS.ErrnoError.prototype.constructor=FS.ErrnoError;[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack=""})},staticInit:()=>{FS.ensureErrnoError();FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={"MEMFS":MEMFS,"WORKERFS":WORKERFS}},init:(input,output,error)=>{FS.init.initialized=true;FS.ensureErrnoError();Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit:()=>{FS.init.initialized=false;for(var i=0;i{var ret=FS.analyzePath(path,dontResolveLastLink);if(!ret.exists){return null}return ret.object},analyzePath:(path,dontResolveLastLink)=>{try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath:(parent,path,canRead,canWrite)=>{parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){}parent=current}return current},createFile:(parent,name,properties,canRead,canWrite)=>{var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile:(parent,name,data,canRead,canWrite,canOwn)=>{var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS_getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){if(typeof data=="string"){var arr=new Array(data.length);for(var i=0,len=data.length;i{var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(!!input,!!output);if(!FS.createDevice.major)FS.createDevice.major=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open:stream=>{stream.seekable=false},close:stream=>{if(output&&output.buffer&&output.buffer.length){output(10)}},read:(stream,buffer,offset,length,pos)=>{var bytesRead=0;for(var i=0;i{for(var i=0;i{if(obj.isDevice||obj.isFolder||obj.link||obj.contents)return true;if(typeof XMLHttpRequest!="undefined"){throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.")}else if(read_){try{obj.contents=intArrayFromString(read_(obj.url),true);obj.usedBytes=obj.contents.length}catch(e){throw new FS.ErrnoError(29)}}else{throw new Error("Cannot load without read() or XMLHttpRequest.")}},createLazyFile:(parent,name,url,canRead,canWrite)=>{function LazyUint8Array(){this.lengthKnown=false;this.chunks=[]}LazyUint8Array.prototype.get=function LazyUint8Array_get(idx){if(idx>this.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr:ptr,allocated:true}};node.stream_ops=stream_ops;return node}};function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt:function(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat:function(func,path,buf){try{var stat=func(path)}catch(e){if(e&&e.node&&PATH.normalize(path)!==PATH.normalize(FS.getPath(e.node))){return-54}throw e}HEAP32[buf>>2]=stat.dev;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAPU32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();tempI64=[Math.floor(atime/1e3)>>>0,(tempDouble=Math.floor(atime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+56>>2]=tempI64[0],HEAP32[buf+60>>2]=tempI64[1];HEAPU32[buf+64>>2]=atime%1e3*1e3;tempI64=[Math.floor(mtime/1e3)>>>0,(tempDouble=Math.floor(mtime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+72>>2]=tempI64[0],HEAP32[buf+76>>2]=tempI64[1];HEAPU32[buf+80>>2]=mtime%1e3*1e3;tempI64=[Math.floor(ctime/1e3)>>>0,(tempDouble=Math.floor(ctime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+88>>2]=tempI64[0],HEAP32[buf+92>>2]=tempI64[1];HEAPU32[buf+96>>2]=ctime%1e3*1e3;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+104>>2]=tempI64[0],HEAP32[buf+108>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.createStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 5:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 6:case 7:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function ___syscall_getcwd(buf,size){try{if(size===0)return-28;var cwd=FS.cwd();var cwdLengthInBytes=lengthBytesUTF8(cwd)+1;if(size>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?SYSCALLS.get():0;return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_readlinkat(dirfd,path,buf,bufsize){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_rmdir(path){try{path=SYSCALLS.getStr(path);FS.rmdir(path);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_unlinkat(dirfd,path,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(flags===0){FS.unlink(path)}else if(flags===512){FS.rmdir(path)}else{abort("Invalid flags passed to unlinkat")}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function __emscripten_throw_longjmp(){throw Infinity}function readI53FromI64(ptr){return HEAPU32[ptr>>2]+HEAP32[ptr+4>>2]*4294967296}function isLeapYear(year){return year%4===0&&(year%100!==0||year%400===0)}var MONTH_DAYS_LEAP_CUMULATIVE=[0,31,60,91,121,152,182,213,244,274,305,335];var MONTH_DAYS_REGULAR_CUMULATIVE=[0,31,59,90,120,151,181,212,243,273,304,334];function ydayFromDate(date){var leap=isLeapYear(date.getFullYear());var monthDaysCumulative=leap?MONTH_DAYS_LEAP_CUMULATIVE:MONTH_DAYS_REGULAR_CUMULATIVE;var yday=monthDaysCumulative[date.getMonth()]+date.getDate()-1;return yday}function __localtime_js(time,tmPtr){var date=new Date(readI53FromI64(time)*1e3);HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+36>>2]=-(date.getTimezoneOffset()*60);var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dst=(summerOffset!=winterOffset&&date.getTimezoneOffset()==Math.min(winterOffset,summerOffset))|0;HEAP32[tmPtr+32>>2]=dst}function stringToNewUTF8(str){var size=lengthBytesUTF8(str)+1;var ret=_malloc(size);if(ret)stringToUTF8(str,ret,size);return ret}function __tzset_js(timezone,daylight,tzname){var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);function extractZone(date){var match=date.toTimeString().match(/\(([A-Za-z ]+)\)$/);return match?match[1]:"GMT"}var winterName=extractZone(winter);var summerName=extractZone(summer);var winterNamePtr=stringToNewUTF8(winterName);var summerNamePtr=stringToNewUTF8(summerName);if(summerOffset>2]=winterNamePtr;HEAPU32[tzname+4>>2]=summerNamePtr}else{HEAPU32[tzname>>2]=summerNamePtr;HEAPU32[tzname+4>>2]=winterNamePtr}}function _emscripten_date_now(){return Date.now()}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function getHeapMax(){return 2147483648}function emscripten_realloc_buffer(size){var b=wasmMemory.buffer;try{wasmMemory.grow(size-b.byteLength+65535>>>16);updateMemoryViews();return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){var oldSize=HEAPU8.length;requestedSize=requestedSize>>>0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}var alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(x+"="+env[x])}getEnvStrings.strings=strings}return getEnvStrings.strings}function stringToAscii(str,buffer){for(var i=0;i>0]=str.charCodeAt(i)}HEAP8[buffer>>0]=0}function _environ_get(__environ,environ_buf){var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;stringToAscii(string,ptr);bufSize+=string.length+1});return 0}function _environ_sizes_get(penviron_count,penviron_buf_size){var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAPU32[penviron_buf_size>>2]=bufSize;return 0}function _proc_exit(code){EXITSTATUS=code;if(!keepRuntimeAlive()){if(Module["onExit"])Module["onExit"](code);ABORT=true}quit_(code,new ExitStatus(code))}function exitJS(status,implicit){EXITSTATUS=status;_proc_exit(status)}var _exit=exitJS;function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function doReadv(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function convertI32PairToI53Checked(lo,hi){return hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var offset=convertI32PairToI53Checked(offset_low,offset_high);if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function doWritev(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(typeof offset!=="undefined"){offset+=curr}}return ret}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function handleException(e){if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)}function stringToUTF8OnStack(str){var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret}function getWasmTableEntry(funcPtr){return wasmTable.get(funcPtr)}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.createPreloadedFile=FS_createPreloadedFile;FS.staticInit();Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["FS_unlink"]=FS.unlink;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createDevice"]=FS.createDevice;var wasmImports={"q":___syscall_fcntl64,"A":___syscall_getcwd,"C":___syscall_ioctl,"m":___syscall_openat,"w":___syscall_readlinkat,"v":___syscall_rmdir,"l":___syscall_unlinkat,"s":__emscripten_throw_longjmp,"x":__localtime_js,"u":__tzset_js,"n":_emscripten_date_now,"B":_emscripten_memcpy_big,"t":_emscripten_resize_heap,"y":_environ_get,"z":_environ_sizes_get,"a":_exit,"k":_fd_close,"p":_fd_read,"r":_fd_seek,"o":_fd_write,"i":invoke_i,"j":invoke_ii,"e":invoke_iii,"c":invoke_iiii,"E":invoke_iiiii,"D":invoke_iiiiiiii,"h":invoke_v,"d":invoke_vi,"g":invoke_vii,"b":invoke_viii,"f":invoke_viiii};var asm=createWasm();var ___wasm_call_ctors=function(){return(___wasm_call_ctors=Module["asm"]["G"]).apply(null,arguments)};var ___errno_location=function(){return(___errno_location=Module["asm"]["H"]).apply(null,arguments)};var _free=function(){return(_free=Module["asm"]["free"]).apply(null,arguments)};var _malloc=function(){return(_malloc=Module["asm"]["I"]).apply(null,arguments)};var _main=Module["_main"]=function(){return(_main=Module["_main"]=Module["asm"]["J"]).apply(null,arguments)};var _setThrew=function(){return(_setThrew=Module["asm"]["L"]).apply(null,arguments)};var stackSave=function(){return(stackSave=Module["asm"]["M"]).apply(null,arguments)};var stackRestore=function(){return(stackRestore=Module["asm"]["N"]).apply(null,arguments)};var stackAlloc=function(){return(stackAlloc=Module["asm"]["O"]).apply(null,arguments)};function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_i(index){var sp=stackSave();try{return getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vi(index,a1){var sp=stackSave();try{getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_v(index){var sp=stackSave();try{getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}Module["addRunDependency"]=addRunDependency;Module["removeRunDependency"]=removeRunDependency;Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createDevice"]=FS.createDevice;Module["FS_unlink"]=FS.unlink;Module["callMain"]=callMain;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["FS"]=FS;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function callMain(args=[]){var entryFunction=_main;args.unshift(thisProgram);var argc=args.length;var argv=stackAlloc((argc+1)*4);var argv_ptr=argv>>2;args.forEach(arg=>{HEAP32[argv_ptr++]=stringToUTF8OnStack(arg)});HEAP32[argv_ptr]=0;try{var ret=entryFunction(argc,argv);exitJS(ret,true);return ret}catch(e){return handleException(e)}}function run(args=arguments_){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();if(shouldRunNow)callMain(args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=true;if(Module["noInitialRun"])shouldRunNow=false;run(); + + + return armtcc.ready +} + +); +})(); +if (typeof exports === 'object' && typeof module === 'object') + module.exports = armtcc; +else if (typeof define === 'function' && define['amd']) + define([], function() { return armtcc; }); +else if (typeof exports === 'object') + exports["armtcc"] = armtcc; diff --git a/src/worker/wasm/arm-tcc.wasm b/src/worker/wasm/arm-tcc.wasm new file mode 100755 index 00000000..8d8fab5f Binary files /dev/null and b/src/worker/wasm/arm-tcc.wasm differ diff --git a/src/worker/workertools.ts b/src/worker/workertools.ts index a105cdf5..8c822c27 100644 --- a/src/worker/workertools.ts +++ b/src/worker/workertools.ts @@ -52,6 +52,8 @@ export const TOOLS = { 'ecs': ecs.assembleECS, 'remote': remote.buildRemote, 'cc7800': cc7800.compileCC7800, + 'armtcc': arm.compileARMTCC, + 'armtcclink': arm.linkARMTCC, } export const TOOL_PRELOADFS = { diff --git a/test/exes/arm32.elf b/test/exes/arm32.elf new file mode 100755 index 00000000..567fd402 Binary files /dev/null and b/test/exes/arm32.elf differ