"use strict"; /** * @constructor * @param {Object=} wasm */ function v86(bus, wasm) { /** @type {boolean} */ this.running = false; /** @type {boolean} */ this.stopping = false; this.tick_counter = 0; this.worker = null; /** @type {CPU} */ this.cpu = new CPU(bus, wasm, () => { this.idle && this.next_tick(0); }); this.bus = bus; bus.register("cpu-init", this.init, this); bus.register("cpu-run", this.run, this); bus.register("cpu-stop", this.stop, this); bus.register("cpu-restart", this.restart, this); this.register_yield(); } v86.prototype.run = function() { this.stopping = false; if(!this.running) { this.running = true; this.bus.send("emulator-started"); } this.next_tick(0); }; v86.prototype.do_tick = function() { if(this.stopping || !this.running) { this.stopping = this.running = false; this.bus.send("emulator-stopped"); return; } this.idle = false; const t = this.cpu.main_run(); this.next_tick(t); }; v86.prototype.next_tick = function(t) { const tick = ++this.tick_counter; this.idle = true; this.yield(t, tick); }; v86.prototype.yield_callback = function(tick) { if(tick === this.tick_counter) { this.do_tick(); } }; v86.prototype.stop = function() { if(this.running) { this.stopping = true; } }; v86.prototype.destroy = function() { this.unregister_yield(); }; v86.prototype.restart = function() { this.cpu.reset_cpu(); this.cpu.load_bios(); }; v86.prototype.init = function(settings) { this.cpu.init(settings, this.bus); this.bus.send("emulator-ready"); }; if(typeof process !== "undefined") { v86.prototype.yield = function(t, tick) { if(t < 1) { global.setImmediate(tick => this.yield_callback(tick), tick); } else { setTimeout(tick => this.yield_callback(tick), t, tick); } }; v86.prototype.register_yield = function() {}; v86.prototype.unregister_yield = function() {}; } else if(typeof Worker !== "undefined") { // XXX: This has a slightly lower throughput compared to window.postMessage function the_worker() { globalThis.onmessage = function(e) { const t = e.data.t; if(t < 1) postMessage(e.data.tick); else setTimeout(() => postMessage(e.data.tick), t); }; } v86.prototype.register_yield = function() { const url = URL.createObjectURL(new Blob(["(" + the_worker.toString() + ")()"], { type: "text/javascript" })); this.worker = new Worker(url); this.worker.onmessage = e => this.yield_callback(e.data); URL.revokeObjectURL(url); }; v86.prototype.yield = function(t, tick) { this.worker.postMessage({ t, tick }); }; v86.prototype.unregister_yield = function() { this.worker.terminate(); this.worker = null; }; } //else if(typeof window !== "undefined" && typeof postMessage !== "undefined") //{ // // setImmediate shim for the browser. // // TODO: Make this deactivatable, for other applications // // using postMessage // // /** @const */ // let MAGIC_POST_MESSAGE = 0xAA55; // // v86.prototype.yield = function(t) // { // // XXX: Use t // window.postMessage(MAGIC_POST_MESSAGE, "*"); // }; // // let tick; // // v86.prototype.register_yield = function() // { // tick = e => // { // if(e.source === window && e.data === MAGIC_POST_MESSAGE) // { // this.do_tick(); // } // }; // // window.addEventListener("message", tick, false); // }; // // v86.prototype.unregister_yield = function() // { // window.removeEventListener("message", tick); // tick = null; // }; //} else { v86.prototype.yield = function(t) { setTimeout(() => { this.do_tick(); }, t); }; v86.prototype.register_yield = function() {}; v86.prototype.unregister_yield = function() {}; } v86.prototype.save_state = function() { // TODO: Should be implemented here, not on cpu return this.cpu.save_state(); }; v86.prototype.restore_state = function(state) { // TODO: Should be implemented here, not on cpu return this.cpu.restore_state(state); }; if(typeof performance === "object" && performance.now) { v86.microtick = performance.now.bind(performance); } else if(typeof require === "function") { const { performance } = require("perf_hooks"); v86.microtick = performance.now.bind(performance); } else if(typeof process === "object" && process.hrtime) { v86.microtick = function() { var t = process.hrtime(); return t[0] * 1000 + t[1] / 1e6; }; } else { v86.microtick = Date.now; }