main.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. "use strict";
  2. /**
  3. * @constructor
  4. * @param {Object=} wasm
  5. */
  6. function v86(bus, wasm)
  7. {
  8. /** @type {boolean} */
  9. this.running = false;
  10. /** @type {boolean} */
  11. this.stopping = false;
  12. this.tick_counter = 0;
  13. this.worker = null;
  14. /** @type {CPU} */
  15. this.cpu = new CPU(bus, wasm, () => { this.idle && this.next_tick(0); });
  16. this.bus = bus;
  17. bus.register("cpu-init", this.init, this);
  18. bus.register("cpu-run", this.run, this);
  19. bus.register("cpu-stop", this.stop, this);
  20. bus.register("cpu-restart", this.restart, this);
  21. this.register_yield();
  22. }
  23. v86.prototype.run = function()
  24. {
  25. this.stopping = false;
  26. if(!this.running)
  27. {
  28. this.running = true;
  29. this.bus.send("emulator-started");
  30. }
  31. this.next_tick(0);
  32. };
  33. v86.prototype.do_tick = function()
  34. {
  35. if(this.stopping || !this.running)
  36. {
  37. this.stopping = this.running = false;
  38. this.bus.send("emulator-stopped");
  39. return;
  40. }
  41. this.idle = false;
  42. const t = this.cpu.main_loop();
  43. this.next_tick(t);
  44. };
  45. v86.prototype.next_tick = function(t)
  46. {
  47. const tick = ++this.tick_counter;
  48. this.idle = true;
  49. this.yield(t, tick);
  50. };
  51. v86.prototype.yield_callback = function(tick)
  52. {
  53. if(tick === this.tick_counter)
  54. {
  55. this.do_tick();
  56. }
  57. };
  58. v86.prototype.stop = function()
  59. {
  60. if(this.running)
  61. {
  62. this.stopping = true;
  63. }
  64. };
  65. v86.prototype.destroy = function()
  66. {
  67. this.unregister_yield();
  68. };
  69. v86.prototype.restart = function()
  70. {
  71. this.cpu.reset_cpu();
  72. this.cpu.load_bios();
  73. };
  74. v86.prototype.init = function(settings)
  75. {
  76. this.cpu.init(settings, this.bus);
  77. this.bus.send("emulator-ready");
  78. };
  79. if(typeof process !== "undefined")
  80. {
  81. v86.prototype.yield = function(t, tick)
  82. {
  83. if(t < 1)
  84. {
  85. global.setImmediate(tick => this.yield_callback(tick), tick);
  86. }
  87. else
  88. {
  89. setTimeout(tick => this.yield_callback(tick), t, tick);
  90. }
  91. };
  92. v86.prototype.register_yield = function() {};
  93. v86.prototype.unregister_yield = function() {};
  94. }
  95. else if(typeof Worker !== "undefined")
  96. {
  97. // XXX: This has a slightly lower throughput compared to window.postMessage
  98. function the_worker()
  99. {
  100. globalThis.onmessage = function(e)
  101. {
  102. const t = e.data.t;
  103. if(t < 1) postMessage(e.data.tick);
  104. else setTimeout(() => postMessage(e.data.tick), t);
  105. };
  106. }
  107. v86.prototype.register_yield = function()
  108. {
  109. const url = URL.createObjectURL(new Blob(["(" + the_worker.toString() + ")()"], { type: "text/javascript" }));
  110. this.worker = new Worker(url);
  111. this.worker.onmessage = e => this.yield_callback(e.data);
  112. URL.revokeObjectURL(url);
  113. };
  114. v86.prototype.yield = function(t, tick)
  115. {
  116. this.worker.postMessage({ t, tick });
  117. };
  118. v86.prototype.unregister_yield = function()
  119. {
  120. this.worker && this.worker.terminate();
  121. this.worker = null;
  122. };
  123. }
  124. //else if(typeof window !== "undefined" && typeof postMessage !== "undefined")
  125. //{
  126. // // setImmediate shim for the browser.
  127. // // TODO: Make this deactivatable, for other applications
  128. // // using postMessage
  129. //
  130. // /** @const */
  131. // let MAGIC_POST_MESSAGE = 0xAA55;
  132. //
  133. // v86.prototype.yield = function(t)
  134. // {
  135. // // XXX: Use t
  136. // window.postMessage(MAGIC_POST_MESSAGE, "*");
  137. // };
  138. //
  139. // let tick;
  140. //
  141. // v86.prototype.register_yield = function()
  142. // {
  143. // tick = e =>
  144. // {
  145. // if(e.source === window && e.data === MAGIC_POST_MESSAGE)
  146. // {
  147. // this.do_tick();
  148. // }
  149. // };
  150. //
  151. // window.addEventListener("message", tick, false);
  152. // };
  153. //
  154. // v86.prototype.unregister_yield = function()
  155. // {
  156. // window.removeEventListener("message", tick);
  157. // tick = null;
  158. // };
  159. //}
  160. else
  161. {
  162. v86.prototype.yield = function(t)
  163. {
  164. setTimeout(() => { this.do_tick(); }, t);
  165. };
  166. v86.prototype.register_yield = function() {};
  167. v86.prototype.unregister_yield = function() {};
  168. }
  169. v86.prototype.save_state = function()
  170. {
  171. // TODO: Should be implemented here, not on cpu
  172. return this.cpu.save_state();
  173. };
  174. v86.prototype.restore_state = function(state)
  175. {
  176. // TODO: Should be implemented here, not on cpu
  177. return this.cpu.restore_state(state);
  178. };
  179. if(typeof performance === "object" && performance.now)
  180. {
  181. v86.microtick = performance.now.bind(performance);
  182. }
  183. else if(typeof require === "function")
  184. {
  185. const { performance } = require("perf_hooks");
  186. v86.microtick = performance.now.bind(performance);
  187. }
  188. else if(typeof process === "object" && process.hrtime)
  189. {
  190. v86.microtick = function()
  191. {
  192. var t = process.hrtime();
  193. return t[0] * 1000 + t[1] / 1e6;
  194. };
  195. }
  196. else
  197. {
  198. v86.microtick = Date.now;
  199. }