import { Sim } from /**/"./Sim.js"; let url = u=>u.startsWith("data:")?u:new URL(u,import.meta.url).toString(); let RESTRICT = {}; let WASM_URL = url(/**/"./core.wasm" ); let WORKER_URL = url(/**/"./CoreWorker.js"); /////////////////////////////////////////////////////////////////////////////// // Core // /////////////////////////////////////////////////////////////////////////////// // Environment manager for simulated Virtual Boys class Core { //////////////////////////////// Constants //////////////////////////////// // States static IDLE = 0; static RUNNING = 1; ///////////////////////////// Static Methods ////////////////////////////// // Create a new instance of Core static create(options) { return new Core(RESTRICT).init(options); } ///////////////////////// Initialization Methods ////////////////////////// // Stub constructor constructor(restrict) { if (restrict != RESTRICT) { throw "Cannot instantiate Core directly. " + "Use Core.create() instead."; } } // Substitute constructor async init(options = {}) { // Configure instance fields this.length = 0; this.onsubscriptions = null; this.resolutions = []; this.state = Core.IDLE; this.worker = new Worker(WORKER_URL); this.worker.onmessage = e=>this.onMessage(e.data); // Issue a create command if ("sims" in options) await this.create(options.sims, WASM_URL); // Only initialize the WebAssembly module else this.send("init", false, { wasm: WASM_URL }); return this; } ///////////////////////////// Event Handlers ////////////////////////////// // Worker message received onMessage(data) { // Process a promised response if ("response" in data) this.resolutions.shift()(data.response); // Process subscriptions if (this.onsubscriptions && data.subscriptions) this.onsubscriptions(data.subscriptions); } ///////////////////////////// Public Methods ////////////////////////////// // Associate two simulations as peers, or remove an association connect(a, b, options = {}) { return this.send({ command: "connect", respond: !("respond" in options) || !!options.respond, sims : [ a, b ] }); } // Create and initialize new simulations async create(sims, wasm) { let numSims = sims===undefined ? 1 : Math.max(0, parseInt(sims) || 0); // Execute the command in the core thread let response = await this.send({ command: "create", sims : numSims, wasm : wasm }); // Process the core thread's response let ret = []; for (let x = 0; x < numSims; x++, this.length++) ret.push(this[this.length] = new Sim(this, response[x], this.length)); return sims === undefined ? ret[0] : ret; } // Delete a simulation destroy(sim, options = {}) { // Configure simulation sim = this[sim] || sim; if (sim.core != this) return; let ptr = sim.destroy(); // State management for (let x = sim.index + 1; x < this.length; x++) (this[x - 1] = this[x]).index--; delete this[--this.length]; // Execute the command on the core thread return this.send({ command: "destroy", respond: !("respond" in options) || !!options.respond, sim : ptr }); } // Attempt to run until the next instruction runNext(a, b, options = {}) { return this.send({ command: "runNext", refresh: !!options.refresh, respond: !("respond" in options) || !!options.respond, sims : [ a, b ] }); } // Execute one instruction singleStep(a, b, options = {}) { return this.send({ command: "singleStep", refresh: !!options.refresh, respond: !("respond" in options) || !!options.respond, sims : [ a, b ] }); } // Unsubscribe from frame data unsubscribe(key, sim = 0) { this.send({ command: "unsubscribe", key : key, respond: false, sim : sim }); } ///////////////////////////// Private Methods ///////////////////////////// // Send a message to the Worker send(data = {}, transfers = []) { // Create the message object Object.assign(data, { respond: !("respond" in data) || !!data.respond, run : !("run" in data) || !!data.run }); // Do not wait on a response if (!data.respond) this.worker.postMessage(data, transfers); // Wait for the response to come back else return new Promise((resolve, reject)=>{ this.resolutions.push(response=>resolve(response)); this.worker.postMessage(data, transfers); }); } } /////////////////////////////////////////////////////////////////////////////// export { Core };