Spaces:
Runtime error
Runtime error
import { CPU } from "./cpu.js"; | |
import { save_state, restore_state } from "./state.js"; | |
export { V86 } from "./browser/starter.js"; | |
/** | |
* @constructor | |
* @param {Object=} wasm | |
*/ | |
export function v86(bus, wasm) | |
{ | |
/** @type {boolean} */ | |
this.running = false; | |
/** @type {boolean} */ | |
this.stopping = false; | |
/** @type {boolean} */ | |
this.idle = true; | |
this.tick_counter = 0; | |
this.worker = null; | |
/** @type {CPU} */ | |
this.cpu = new CPU(bus, wasm, () => { this.idle && this.next_tick(0); }); | |
this.bus = bus; | |
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_loop(); | |
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) | |
{ | |
/* global global */ | |
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() | |
{ | |
let timeout; | |
globalThis.onmessage = function(e) | |
{ | |
const t = e.data.t; | |
timeout = timeout && clearTimeout(timeout); | |
if(t < 1) postMessage(e.data.tick); | |
else timeout = 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 && 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 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 save_state(this.cpu); | |
}; | |
v86.prototype.restore_state = function(state) | |
{ | |
// TODO: Should be implemented here, not on cpu | |
return restore_state(this.cpu, state); | |
}; | |
/* global require */ | |
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; | |
} | |