"use strict"; /** * @constructor * @param {Object=} wasm */ function v86(bus, wasm) { /** @type {boolean} */ this.running = false; /** @type {boolean} */ this.stopped = false; /** @type {CPU} */ this.cpu = new CPU(bus, wasm); 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_tick(); } v86.prototype.run = function() { this.stopped = false; if(!this.running) { this.bus.send("emulator-started"); this.fast_next_tick(); } }; v86.prototype.do_tick = function() { if(this.stopped) { this.stopped = this.running = false; this.bus.send("emulator-stopped"); return; } this.running = true; var dt = this.cpu.main_run(); if(dt <= 0) { this.fast_next_tick(); } else { this.next_tick(dt); } }; v86.prototype.stop = function() { if(this.running) { this.stopped = true; } }; v86.prototype.destroy = function() { this.unregister_tick(); }; v86.prototype.restart = function() { this.cpu.reset(); this.cpu.load_bios(); }; v86.prototype.init = function(settings) { this.cpu.init(settings, this.bus); this.bus.send("emulator-ready"); }; if(typeof setImmediate !== "undefined") { /** @this {v86} */ var fast_next_tick = function() { setImmediate(() => { this.do_tick(); }); }; /** @this {v86} */ var register_tick = function() {}; /** @this {v86} */ var unregister_tick = function() {}; } 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; /** @this {v86} */ fast_next_tick = function() { window.postMessage(MAGIC_POST_MESSAGE, "*"); }; let tick; /** @this {v86} */ register_tick = function() { tick = e => { if(e.source === window && e.data === MAGIC_POST_MESSAGE) { this.do_tick(); } }; window.addEventListener("message", tick, false); }; /** @this {v86} */ unregister_tick = function() { window.removeEventListener("message", tick); tick = null; }; } else { /** @this {v86} */ fast_next_tick = function() { setTimeout(() => { this.do_tick(); }, 0); }; /** @this {v86} */ register_tick = function() {}; /** @this {v86} */ unregister_tick = function() {}; } v86.prototype.fast_next_tick = fast_next_tick; v86.prototype.register_tick = register_tick; v86.prototype.unregister_tick = unregister_tick; if(typeof document !== "undefined" && typeof document.hidden === "boolean") { /** @this {v86} */ var next_tick = function(t) { if(t < 4 || document.hidden) { // Avoid sleeping for 1 second (happens if page is not // visible), it can break boot processes. Also don't try to // sleep for less than 4ms, since the value is clamped up this.fast_next_tick(); } else { setTimeout(() => { this.do_tick(); }, t); } }; } else { // In environments that aren't browsers, we might as well use setTimeout /** @this {v86} */ next_tick = function(t) { setTimeout(() => { this.do_tick(); }, t); }; } v86.prototype.next_tick = next_tick; 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; }