main.js 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067
  1. "use strict";
  2. (function()
  3. {
  4. /** @const */
  5. var ON_LOCALHOST = !location.hostname.endsWith("copy.sh");
  6. /**
  7. * @return {Object.<string, string>}
  8. */
  9. function get_query_arguments()
  10. {
  11. var query = location.search.substr(1).split("&");
  12. var parameters = {};
  13. for(var i = 0; i < query.length; i++)
  14. {
  15. var param = query[i].split("=");
  16. parameters[param[0]] = decodeURIComponent(param.slice(1).join("="));
  17. }
  18. return parameters;
  19. }
  20. function set_title(text)
  21. {
  22. document.title = text + " - Virtual x86" + (DEBUG ? " - debug" : "");
  23. const description = document.querySelector("meta[name=description]");
  24. description && (description.content = "Running " + text);
  25. }
  26. function format_timestamp(time)
  27. {
  28. if(time < 60)
  29. {
  30. return time + "s";
  31. }
  32. else if(time < 3600)
  33. {
  34. return (time / 60 | 0) + "m " + v86util.pad0(time % 60, 2) + "s";
  35. }
  36. else
  37. {
  38. return (time / 3600 | 0) + "h " +
  39. v86util.pad0((time / 60 | 0) % 60, 2) + "m " +
  40. v86util.pad0(time % 60, 2) + "s";
  41. }
  42. }
  43. var progress_ticks = 0;
  44. function show_progress(e)
  45. {
  46. var el = $("loading");
  47. el.style.display = "block";
  48. if(e.file_name.endsWith(".wasm"))
  49. {
  50. const parts = e.file_name.split("/");
  51. el.textContent = "Fetching " + parts[parts.length - 1] + " ...";
  52. return;
  53. }
  54. if(e.file_index === e.file_count - 1 && e.loaded >= e.total - 2048)
  55. {
  56. // last file is (almost) loaded
  57. el.textContent = "Done downloading. Starting now ...";
  58. return;
  59. }
  60. var line = "Downloading images ";
  61. if(typeof e.file_index === "number" && e.file_count)
  62. {
  63. line += "[" + (e.file_index + 1) + "/" + e.file_count + "] ";
  64. }
  65. if(e.total && typeof e.loaded === "number")
  66. {
  67. var per100 = Math.floor(e.loaded / e.total * 100);
  68. per100 = Math.min(100, Math.max(0, per100));
  69. var per50 = Math.floor(per100 / 2);
  70. line += per100 + "% [";
  71. line += "#".repeat(per50);
  72. line += " ".repeat(50 - per50) + "]";
  73. }
  74. else
  75. {
  76. line += ".".repeat(progress_ticks++ % 50);
  77. }
  78. el.textContent = line;
  79. }
  80. function $(id)
  81. {
  82. return document.getElementById(id);
  83. }
  84. function onload()
  85. {
  86. if(!window.WebAssembly)
  87. {
  88. alert("Your browser is not supported because it doesn't support WebAssembly");
  89. return;
  90. }
  91. const script = document.createElement("script");
  92. script.src = "build/xterm.js";
  93. script.async = true;
  94. document.body.appendChild(script);
  95. var settings = {};
  96. $("start_emulation").onclick = function()
  97. {
  98. $("boot_options").style.display = "none";
  99. set_profile("custom");
  100. var images = [];
  101. var last_file;
  102. var floppy_file = $("floppy_image").files[0];
  103. if(floppy_file)
  104. {
  105. last_file = floppy_file;
  106. settings.fda = { buffer: floppy_file };
  107. }
  108. var cd_file = $("cd_image").files[0];
  109. if(cd_file)
  110. {
  111. last_file = cd_file;
  112. settings.cdrom = { buffer: cd_file };
  113. }
  114. var hda_file = $("hda_image").files[0];
  115. if(hda_file)
  116. {
  117. last_file = hda_file;
  118. settings.hda = { buffer: hda_file };
  119. }
  120. var hdb_file = $("hdb_image") && $("hdb_image").files[0];
  121. if(hdb_file)
  122. {
  123. last_file = hdb_file;
  124. settings.hdb = { buffer: hdb_file };
  125. }
  126. if($("multiboot_image"))
  127. {
  128. var multiboot_file = $("multiboot_image").files[0];
  129. if(multiboot_file)
  130. {
  131. last_file = multiboot_file;
  132. settings.multiboot = { buffer: multiboot_file };
  133. }
  134. }
  135. if(last_file)
  136. {
  137. set_title(last_file.name);
  138. }
  139. start_emulation(settings);
  140. };
  141. if(DEBUG)
  142. {
  143. debug_onload(settings);
  144. }
  145. const query_args = get_query_arguments();
  146. const host = query_args["cdn"] || (ON_LOCALHOST ? "images/" : "//k.copy.sh/");
  147. // Abandonware OS images are from https://winworldpc.com/library/operating-systems
  148. var oses = [
  149. {
  150. id: "archlinux",
  151. name: "Arch Linux",
  152. memory_size: 512 * 1024 * 1024,
  153. vga_memory_size: 8 * 1024 * 1024,
  154. state: {
  155. url: host + "arch_state.bin.zst",
  156. },
  157. filesystem: {
  158. baseurl: host + "arch/",
  159. },
  160. },
  161. {
  162. id: "archlinux-boot",
  163. name: "Arch Linux",
  164. memory_size: 512 * 1024 * 1024,
  165. vga_memory_size: 8 * 1024 * 1024,
  166. filesystem: {
  167. baseurl: host + "arch/",
  168. basefs: { url: host + "fs.json", },
  169. },
  170. cmdline: [
  171. "rw apm=off vga=0x344 video=vesafb:ypan,vremap:8",
  172. "root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose",
  173. "mitigations=off audit=0",
  174. "init_on_free=on",
  175. "tsc=reliable",
  176. "random.trust_cpu=on",
  177. "nowatchdog",
  178. "init=/usr/bin/init-openrc net.ifnames=0 biosdevname=0",
  179. ].join(" "),
  180. bzimage_initrd_from_filesystem: true,
  181. },
  182. {
  183. id: "copy/skiffos",
  184. name: "SkiffOS",
  185. cdrom: {
  186. url: host + "skiffos.iso",
  187. size: 124672000,
  188. async: true,
  189. fixed_chunk_size: 1024 * 1024,
  190. use_parts: !ON_LOCALHOST,
  191. },
  192. memory_size: 512 * 1024 * 1024,
  193. },
  194. {
  195. id: "serenity",
  196. name: "SerenityOS",
  197. hda: {
  198. url: host + "serenity-v2.img",
  199. size: 700448768,
  200. async: true,
  201. fixed_chunk_size: 1024 * 1024,
  202. use_parts: !ON_LOCALHOST,
  203. },
  204. memory_size: 512 * 1024 * 1024,
  205. state: { url: host + "serenity_state-v3.bin.zst", },
  206. homepage: "https://serenityos.org/",
  207. mac_address_translation: true,
  208. },
  209. {
  210. id: "serenity-boot",
  211. name: "SerenityOS",
  212. hda: {
  213. url: host + "serenity-v2.img",
  214. size: 700448768,
  215. async: true,
  216. fixed_chunk_size: 1024 * 1024,
  217. use_parts: !ON_LOCALHOST,
  218. },
  219. memory_size: 512 * 1024 * 1024,
  220. homepage: "https://serenityos.org/",
  221. },
  222. {
  223. id: "serenity-old",
  224. name: "SerenityOS",
  225. hda: {
  226. url: host + "serenity.img",
  227. size: 876 * 1024 * 1024,
  228. async: true,
  229. fixed_chunk_size: 1024 * 1024,
  230. use_parts: !ON_LOCALHOST,
  231. },
  232. memory_size: 512 * 1024 * 1024,
  233. state: { url: host + "serenity_state-v2.bin.zst", },
  234. homepage: "https://serenityos.org/",
  235. },
  236. {
  237. id: "serenity-old-boot",
  238. name: "SerenityOS",
  239. hda: {
  240. url: host + "serenity.img",
  241. size: 876 * 1024 * 1024,
  242. async: true,
  243. fixed_chunk_size: 1024 * 1024,
  244. use_parts: !ON_LOCALHOST,
  245. },
  246. memory_size: 512 * 1024 * 1024,
  247. homepage: "https://serenityos.org/",
  248. },
  249. {
  250. id: "helenos",
  251. memory_size: 256 * 1024 * 1024,
  252. cdrom: {
  253. url: host + "HelenOS-0.11.2-ia32.iso",
  254. size: 25765888,
  255. async: false,
  256. },
  257. name: "HelenOS",
  258. homepage: "http://www.helenos.org/",
  259. },
  260. {
  261. id: "fiwix",
  262. memory_size: 256 * 1024 * 1024,
  263. hda: {
  264. url: host + "fiwixos-3.2-i386.img",
  265. size: 1024 * 1024 * 1024,
  266. async: true,
  267. fixed_chunk_size: 1024 * 1024,
  268. use_parts: !ON_LOCALHOST,
  269. },
  270. name: "FiwixOS",
  271. homepage: "https://www.fiwix.org/",
  272. },
  273. {
  274. id: "haiku",
  275. memory_size: 512 * 1024 * 1024,
  276. hda: {
  277. url: host + "haiku-v2.img",
  278. size: 1 * 1024 * 1024 * 1024,
  279. async: true,
  280. fixed_chunk_size: 1024 * 1024,
  281. use_parts: !ON_LOCALHOST,
  282. },
  283. state: {
  284. url: host + "haiku_state-v2.bin.zst",
  285. },
  286. name: "Haiku",
  287. homepage: "https://www.haiku-os.org/",
  288. },
  289. {
  290. id: "haiku-boot",
  291. memory_size: 512 * 1024 * 1024,
  292. hda: {
  293. url: host + "haiku-v2.img",
  294. size: 1 * 1024 * 1024 * 1024,
  295. async: true,
  296. fixed_chunk_size: 1024 * 1024,
  297. use_parts: !ON_LOCALHOST,
  298. },
  299. name: "Haiku",
  300. homepage: "https://www.haiku-os.org/",
  301. },
  302. {
  303. id: "msdos",
  304. hda: {
  305. url: host + "msdos.img",
  306. size: 8 * 1024 * 1024,
  307. async: false,
  308. },
  309. boot_order: 0x132,
  310. name: "MS-DOS",
  311. },
  312. {
  313. id: "freedos",
  314. fda: {
  315. url: host + "freedos722.img",
  316. size: 737280,
  317. async: false,
  318. },
  319. name: "FreeDOS",
  320. },
  321. {
  322. id: "psychdos",
  323. hda: {
  324. url: host + "psychdos.img",
  325. size: 549453824,
  326. async: true,
  327. fixed_chunk_size: 256 * 1024,
  328. use_parts: !ON_LOCALHOST,
  329. },
  330. name: "PsychDOS",
  331. homepage: "https://psychoslinux.gitlab.io/DOS/INDEX.HTM",
  332. },
  333. {
  334. id: "oberon",
  335. hda: {
  336. url: host + "oberon.img",
  337. size: 24 * 1024 * 1024,
  338. async: false,
  339. },
  340. name: "Oberon",
  341. },
  342. {
  343. id: "windows1",
  344. fda: {
  345. url: host + "windows101.img",
  346. size: 1474560,
  347. async: false,
  348. },
  349. name: "Windows",
  350. },
  351. {
  352. id: "linux26",
  353. cdrom: {
  354. url: host + "linux.iso",
  355. size: 6547456,
  356. async: false,
  357. },
  358. name: "Linux",
  359. },
  360. {
  361. id: "linux3",
  362. cdrom: {
  363. url: host + "linux3.iso",
  364. size: 8624128,
  365. async: false,
  366. },
  367. name: "Linux",
  368. },
  369. {
  370. id: "linux4",
  371. cdrom: {
  372. url: host + "linux4.iso",
  373. size: 7731200,
  374. async: false,
  375. },
  376. name: "Linux",
  377. filesystem: {},
  378. },
  379. {
  380. id: "buildroot",
  381. bzimage: {
  382. url: host + "buildroot-bzimage.bin",
  383. size: 5166352,
  384. async: false,
  385. },
  386. name: "Buildroot Linux",
  387. filesystem: {},
  388. cmdline: "tsc=reliable mitigations=off random.trust_cpu=on",
  389. },
  390. {
  391. id: "nodeos",
  392. bzimage: {
  393. url: host + "nodeos-kernel.bin",
  394. size: 14452000,
  395. async: false,
  396. },
  397. name: "NodeOS",
  398. cmdline: "tsc=reliable mitigations=off random.trust_cpu=on",
  399. },
  400. {
  401. id: "dsl",
  402. memory_size: 256 * 1024 * 1024,
  403. cdrom: {
  404. url: host + "dsl-4.11.rc2.iso",
  405. size: 52824064,
  406. async: false,
  407. },
  408. name: "Damn Small Linux",
  409. homepage: "http://www.damnsmalllinux.org/",
  410. },
  411. {
  412. id: "minix",
  413. name: "Minix",
  414. memory_size: 256 * 1024 * 1024,
  415. cdrom: {
  416. url: host + "minix-3.3.0.iso",
  417. size: 605581312,
  418. async: true,
  419. fixed_chunk_size: 1024 * 1024,
  420. use_parts: !ON_LOCALHOST,
  421. },
  422. homepage: "https://www.minix3.org/",
  423. },
  424. {
  425. id: "kolibrios",
  426. fda: {
  427. url: ON_LOCALHOST ?
  428. host + "kolibri.img" :
  429. "//builds.kolibrios.org/eng/data/data/kolibri.img",
  430. size: 1474560,
  431. async: false,
  432. },
  433. name: "KolibriOS",
  434. homepage: "https://kolibrios.org/en/",
  435. },
  436. {
  437. id: "kolibrios-fallback",
  438. fda: {
  439. url: host + "kolibri.img",
  440. size: 1474560,
  441. async: false,
  442. },
  443. name: "KolibriOS",
  444. },
  445. {
  446. id: "openbsd",
  447. hda: {
  448. url: host + "openbsd.img",
  449. size: 1073741824,
  450. async: true,
  451. fixed_chunk_size: 1024 * 1024,
  452. use_parts: !ON_LOCALHOST,
  453. },
  454. state: {
  455. url: host + "openbsd_state.bin.zst",
  456. },
  457. memory_size: 256 * 1024 * 1024,
  458. name: "OpenBSD",
  459. },
  460. {
  461. id: "openbsd-boot",
  462. hda: {
  463. url: host + "openbsd.img",
  464. size: 1073741824,
  465. async: true,
  466. fixed_chunk_size: 1024 * 1024,
  467. use_parts: !ON_LOCALHOST,
  468. },
  469. memory_size: 256 * 1024 * 1024,
  470. name: "OpenBSD",
  471. //acpi: true, // doesn't seem to work
  472. },
  473. {
  474. id: "netbsd",
  475. hda: {
  476. url: host + "netbsd.img",
  477. size: 511000064,
  478. async: true,
  479. fixed_chunk_size: 1024 * 1024,
  480. use_parts: !ON_LOCALHOST,
  481. },
  482. memory_size: 256 * 1024 * 1024,
  483. name: "NetBSD",
  484. },
  485. {
  486. id: "solos",
  487. fda: {
  488. url: host + "os8.img",
  489. async: false,
  490. size: 1474560,
  491. },
  492. name: "Sol OS",
  493. homepage: "http://oby.ro/os/",
  494. },
  495. {
  496. id: "bootchess",
  497. fda: {
  498. url: host + "bootchess.img",
  499. async: false,
  500. size: 1474560,
  501. },
  502. name: "BootChess",
  503. homepage: "http://www.pouet.net/prod.php?which=64962",
  504. },
  505. {
  506. id: "bootbasic",
  507. fda: {
  508. url: host + "bootbasic.img",
  509. async: false,
  510. size: 1474560,
  511. },
  512. name: "bootBASIC",
  513. homepage: "https://github.com/nanochess/bootBASIC",
  514. },
  515. {
  516. id: "sectorlisp",
  517. fda: {
  518. url: host + "sectorlisp-friendly.bin",
  519. async: false,
  520. size: 512,
  521. },
  522. name: "SectorLISP",
  523. homepage: "https://justine.lol/sectorlisp2/",
  524. },
  525. {
  526. id: "sectorforth",
  527. fda: {
  528. url: host + "sectorforth.img",
  529. async: false,
  530. size: 512,
  531. },
  532. name: "sectorforth",
  533. homepage: "https://github.com/cesarblum/sectorforth",
  534. },
  535. {
  536. id: "floppybird",
  537. fda: {
  538. url: host + "floppybird.img",
  539. async: false,
  540. size: 1474560,
  541. },
  542. name: "Floppy Bird",
  543. homepage: "http://mihail.co/floppybird",
  544. },
  545. {
  546. id: "windows2000",
  547. memory_size: 512 * 1024 * 1024,
  548. hda: {
  549. url: host + "windows2k.img",
  550. size: 2 * 1024 * 1024 * 1024,
  551. async: true,
  552. fixed_chunk_size: 256 * 1024,
  553. use_parts: !ON_LOCALHOST,
  554. },
  555. name: "Windows 2000",
  556. state: {
  557. url: host + "windows2k_state-v2.bin.zst",
  558. },
  559. mac_address_translation: true,
  560. },
  561. {
  562. id: "windows2000-boot",
  563. memory_size: 512 * 1024 * 1024,
  564. hda: {
  565. url: host + "windows2k.img",
  566. size: 2 * 1024 * 1024 * 1024,
  567. async: true,
  568. fixed_chunk_size: 256 * 1024,
  569. use_parts: !ON_LOCALHOST,
  570. },
  571. boot_order: 0x132,
  572. name: "Windows 2000",
  573. },
  574. {
  575. id: "windowsnt4",
  576. memory_size: 512 * 1024 * 1024,
  577. hda: {
  578. url: host + "winnt4_noacpi.img",
  579. size: 523837440,
  580. async: true,
  581. fixed_chunk_size: 256 * 1024,
  582. use_parts: !ON_LOCALHOST,
  583. },
  584. name: "Windows NT 4.0",
  585. cpuid_level: 2,
  586. },
  587. {
  588. id: "windowsnt3",
  589. memory_size: 256 * 1024 * 1024,
  590. hda: {
  591. url: host + "winnt31.img",
  592. size: 87 * 1024 * 1024,
  593. async: true,
  594. fixed_chunk_size: 256 * 1024,
  595. use_parts: !ON_LOCALHOST,
  596. },
  597. name: "Windows NT 3.1",
  598. },
  599. {
  600. id: "windows98",
  601. memory_size: 128 * 1024 * 1024,
  602. hda: {
  603. url: host + "windows98.img",
  604. size: 300 * 1024 * 1024,
  605. async: true,
  606. fixed_chunk_size: 256 * 1024,
  607. use_parts: !ON_LOCALHOST,
  608. },
  609. name: "Windows 98",
  610. state: {
  611. url: host + "windows98_state.bin.zst",
  612. },
  613. mac_address_translation: true,
  614. },
  615. {
  616. id: "windows98-boot",
  617. memory_size: 128 * 1024 * 1024,
  618. hda: {
  619. url: host + "windows98.img",
  620. size: 300 * 1024 * 1024,
  621. async: true,
  622. fixed_chunk_size: 256 * 1024,
  623. use_parts: !ON_LOCALHOST,
  624. },
  625. name: "Windows 98",
  626. },
  627. {
  628. id: "windows95",
  629. memory_size: 32 * 1024 * 1024,
  630. hda: {
  631. url: host + "w95.img",
  632. size: 242049024,
  633. async: true,
  634. fixed_chunk_size: 256 * 1024,
  635. use_parts: !ON_LOCALHOST,
  636. },
  637. name: "Windows 95",
  638. state: {
  639. url: host + "windows95_state.bin.zst",
  640. },
  641. },
  642. {
  643. id: "windows95-boot",
  644. memory_size: 32 * 1024 * 1024,
  645. hda: {
  646. url: host + "w95.img",
  647. size: 242049024,
  648. async: true,
  649. fixed_chunk_size: 256 * 1024,
  650. use_parts: !ON_LOCALHOST,
  651. },
  652. name: "Windows 95",
  653. },
  654. {
  655. id: "windows30",
  656. memory_size: 64 * 1024 * 1024,
  657. cdrom: {
  658. url: host + "Win30.iso",
  659. async: false,
  660. },
  661. name: "Windows 3.0",
  662. },
  663. {
  664. id: "windows31",
  665. memory_size: 64 * 1024 * 1024,
  666. hda: {
  667. url: host + "win31.img",
  668. async: false,
  669. size: 34463744,
  670. },
  671. name: "Windows 3.1",
  672. },
  673. {
  674. id: "freebsd",
  675. memory_size: 256 * 1024 * 1024,
  676. hda: {
  677. url: host + "freebsd.img",
  678. size: 2147483648,
  679. async: true,
  680. fixed_chunk_size: 1024 * 1024,
  681. use_parts: !ON_LOCALHOST,
  682. },
  683. state: {
  684. url: host + "freebsd_state.bin.zst",
  685. },
  686. name: "FreeBSD",
  687. },
  688. {
  689. id: "freebsd-boot",
  690. memory_size: 256 * 1024 * 1024,
  691. hda: {
  692. url: host + "freebsd.img",
  693. size: 2147483648,
  694. async: true,
  695. fixed_chunk_size: 1024 * 1024,
  696. use_parts: !ON_LOCALHOST,
  697. },
  698. name: "FreeBSD",
  699. },
  700. {
  701. id: "reactos-livecd",
  702. memory_size: 256 * 1024 * 1024,
  703. hda: {
  704. url: host + "reactos-livecd-0.4.15-dev-73-g03c09c9-x86-gcc-lin-dbg.iso",
  705. size: 250609664,
  706. async: true,
  707. fixed_chunk_size: 1024 * 1024,
  708. use_parts: !ON_LOCALHOST,
  709. },
  710. name: "ReactOS",
  711. homepage: "https://reactos.org/",
  712. },
  713. {
  714. id: "reactos",
  715. memory_size: 512 * 1024 * 1024,
  716. hda: {
  717. url: host + "reactos.img",
  718. size: 500 * 1024 * 1024,
  719. async: true,
  720. fixed_chunk_size: 1024 * 1024,
  721. use_parts: !ON_LOCALHOST,
  722. },
  723. state: {
  724. url: host + "reactos_state.bin.zst",
  725. },
  726. mac_address_translation: true,
  727. name: "ReactOS",
  728. homepage: "https://reactos.org/",
  729. },
  730. {
  731. id: "reactos-boot",
  732. memory_size: 512 * 1024 * 1024,
  733. hda: {
  734. url: host + "reactos.img",
  735. size: 500 * 1024 * 1024,
  736. async: true,
  737. fixed_chunk_size: 1024 * 1024,
  738. use_parts: !ON_LOCALHOST,
  739. },
  740. name: "ReactOS",
  741. homepage: "https://reactos.org/",
  742. },
  743. {
  744. id: "skift",
  745. cdrom: {
  746. url: host + "skift-20200910.iso",
  747. size: 64452608,
  748. async: false,
  749. },
  750. name: "Skift",
  751. homepage: "https://skiftos.org/",
  752. },
  753. {
  754. id: "snowdrop",
  755. fda: {
  756. url: host + "snowdrop.img",
  757. size: 1440 * 1024,
  758. async: false,
  759. },
  760. name: "Snowdrop",
  761. homepage: "http://www.sebastianmihai.com/snowdrop/",
  762. },
  763. {
  764. id: "openwrt",
  765. hda: {
  766. url: host + "openwrt-18.06.1-x86-legacy-combined-squashfs.img",
  767. size: 19846474,
  768. async: false,
  769. },
  770. name: "OpenWrt",
  771. },
  772. {
  773. id: "qnx",
  774. fda: {
  775. url: host + "qnx-demo-network-4.05.img",
  776. size: 1474560,
  777. async: false
  778. },
  779. name: "QNX 4.05",
  780. },
  781. {
  782. id: "9front",
  783. memory_size: 128 * 1024 * 1024,
  784. hda: {
  785. url: host + "9front-8963.f84cf1e60427675514fb056cc1723e45da01e043.386.iso",
  786. size: 477452288,
  787. async: true,
  788. fixed_chunk_size: 1024 * 1024,
  789. use_parts: !ON_LOCALHOST,
  790. },
  791. state: {
  792. url: host + "9front_state-v2.bin.zst",
  793. },
  794. acpi: true,
  795. name: "9front",
  796. homepage: "https://9front.org/",
  797. },
  798. {
  799. id: "9front-boot",
  800. memory_size: 128 * 1024 * 1024,
  801. hda: {
  802. url: host + "9front-8963.f84cf1e60427675514fb056cc1723e45da01e043.386.iso",
  803. size: 477452288,
  804. async: true,
  805. fixed_chunk_size: 1024 * 1024,
  806. use_parts: !ON_LOCALHOST,
  807. },
  808. acpi: true,
  809. name: "9front",
  810. homepage: "https://9front.org/",
  811. },
  812. {
  813. id: "mobius",
  814. fda: {
  815. url: host + "mobius-fd-release5.img",
  816. size: 1474560,
  817. async: false,
  818. },
  819. name: "Mobius",
  820. },
  821. {
  822. id: "android",
  823. memory_size: 512 * 1024 * 1024,
  824. cdrom: {
  825. url: host + "android-x86-1.6-r2.iso",
  826. size: 54661120,
  827. async: true,
  828. fixed_chunk_size: 1024 * 1024,
  829. use_parts: !ON_LOCALHOST,
  830. },
  831. name: "Android",
  832. },
  833. {
  834. id: "android4",
  835. memory_size: 512 * 1024 * 1024,
  836. cdrom: {
  837. url: host + "android_x86_nonsse3_4.4r1_20140904.iso",
  838. size: 247463936,
  839. async: true,
  840. fixed_chunk_size: 1024 * 1024,
  841. use_parts: !ON_LOCALHOST,
  842. },
  843. name: "Android",
  844. },
  845. {
  846. id: "tinycore",
  847. memory_size: 256 * 1024 * 1024,
  848. hda: {
  849. url: host + "TinyCore-11.0.iso",
  850. async: false,
  851. },
  852. name: "Tinycore",
  853. homepage: "http://www.tinycorelinux.net/",
  854. },
  855. {
  856. id: "freenos",
  857. memory_size: 256 * 1024 * 1024,
  858. cdrom: {
  859. url: host + "FreeNOS-1.0.3.iso",
  860. async: false,
  861. size: 11014144,
  862. },
  863. name: "FreeNOS",
  864. acpi: true,
  865. homepage: "http://www.freenos.org/",
  866. },
  867. ];
  868. if(DEBUG)
  869. {
  870. // see tests/kvm-unit-tests/x86/
  871. var tests = [
  872. "realmode",
  873. // All tests below require an APIC
  874. "cmpxchg8b",
  875. "port80",
  876. "setjmp",
  877. "sieve",
  878. "hypercall", // crashes
  879. "init", // stops execution
  880. "msr", // TODO: Expects 64 bit msrs
  881. "smap", // test stops, SMAP not enabled
  882. "tsc_adjust", // TODO: IA32_TSC_ADJUST
  883. "tsc", // TODO: rdtscp
  884. "rmap_chain", // crashes
  885. "memory", // missing mfence (uninteresting)
  886. "taskswitch", // TODO: Jump
  887. "taskswitch2", // TODO: Call TSS
  888. "eventinj", // Missing #nt
  889. "ioapic",
  890. "apic",
  891. ];
  892. for(let test of tests)
  893. {
  894. oses.push({
  895. name: "Test case: " + test,
  896. id: "test-" + test,
  897. memory_size: 128 * 1024 * 1024,
  898. multiboot: { url: "tests/kvm-unit-tests/x86/" + test + ".flat", }
  899. });
  900. }
  901. }
  902. var profile = query_args["profile"];
  903. if(!profile && !DEBUG)
  904. {
  905. const link = document.createElement("link");
  906. link.rel = "prefetch";
  907. link.href = "build/v86.wasm";
  908. document.head.appendChild(link);
  909. }
  910. if(query_args["use_bochs_bios"])
  911. {
  912. settings.use_bochs_bios = true;
  913. }
  914. const m = parseInt(query_args["m"], 10);
  915. if(m > 0)
  916. {
  917. settings.memory_size = Math.max(16, m) * 1024 * 1024;
  918. }
  919. const vram = parseInt(query_args["vram"], 10);
  920. if(vram > 0)
  921. {
  922. settings.vga_memory_size = vram * 1024 * 1024;
  923. }
  924. settings.networking_proxy = query_args["networking_proxy"];
  925. settings.audio = query_args["audio"] !== "0";
  926. settings.acpi = query_args["acpi"];
  927. for(var i = 0; i < oses.length; i++)
  928. {
  929. var infos = oses[i];
  930. if(profile === infos.id)
  931. {
  932. start_profile(infos);
  933. return;
  934. }
  935. var element = $("start_" + infos.id);
  936. if(element)
  937. {
  938. element.onclick = function(infos, element, e)
  939. {
  940. e.preventDefault();
  941. set_profile(infos.id);
  942. element.blur();
  943. start_profile(infos);
  944. }.bind(this, infos, element);
  945. }
  946. }
  947. if(profile === "custom")
  948. {
  949. if(query_args["hda.url"])
  950. {
  951. settings.hda = {
  952. size: parseInt(query_args["hda.size"], 10) || undefined,
  953. url: query_args["hda.url"],
  954. async: true,
  955. };
  956. }
  957. if(query_args["cdrom.url"])
  958. {
  959. settings.cdrom = {
  960. size: parseInt(query_args["cdrom.size"], 10) || undefined,
  961. url: query_args["cdrom.url"],
  962. async: true,
  963. };
  964. }
  965. if(query_args["fda.url"])
  966. {
  967. settings.fda = {
  968. size: parseInt(query_args["fda.size"], 10) || undefined,
  969. url: query_args["fda.url"],
  970. async: false,
  971. };
  972. }
  973. if(settings.fda || settings.cdrom || settings.hda)
  974. {
  975. $("boot_options").style.display = "none";
  976. start_emulation(settings, done);
  977. }
  978. }
  979. else if(/^[a-zA-Z0-9\-_]+\/[a-zA-Z0-9\-_]+$/g.test(profile))
  980. {
  981. // experimental: server that allows user-uploaded images
  982. const base = "https://v86-user-images.b-cdn.net/" + profile;
  983. fetch(base + "/profile.json")
  984. .then(response => response.json())
  985. .then(p => {
  986. function handle_image(o)
  987. {
  988. return o && { url: base + "/" + o["url"], async: o["async"], size: o["size"] };
  989. }
  990. start_profile({
  991. id: p["id"],
  992. name: p["name"],
  993. memory_size: p["memory_size"],
  994. vga_memory_size: p["vga_memory_size"],
  995. acpi: p["acpi"],
  996. boot_order: p["boot_order"],
  997. hda: handle_image(p["hda"]),
  998. cdrom: handle_image(p["cdrom"]),
  999. fda: handle_image(p["fda"]),
  1000. multiboot: handle_image(p["multiboot"]),
  1001. bzimage: handle_image(p["bzimage"]),
  1002. initrd: handle_image(p["initrd"]),
  1003. });
  1004. })
  1005. .catch(e => alert("Profile not found: " + profile));
  1006. }
  1007. function start_profile(infos)
  1008. {
  1009. $("boot_options").style.display = "none";
  1010. set_title(infos.name);
  1011. settings.filesystem = infos.filesystem;
  1012. if(infos.state)
  1013. {
  1014. $("reset").style.display = "none";
  1015. settings.initial_state = infos.state;
  1016. }
  1017. settings.fda = infos.fda;
  1018. settings.cdrom = infos.cdrom;
  1019. settings.hda = infos.hda;
  1020. settings.multiboot = infos.multiboot;
  1021. settings.bzimage = infos.bzimage;
  1022. settings.initrd = infos.initrd;
  1023. settings.cmdline = infos.cmdline;
  1024. settings.bzimage_initrd_from_filesystem = infos.bzimage_initrd_from_filesystem;
  1025. settings.mac_address_translation = infos.mac_address_translation;
  1026. settings.cpuid_level = infos.cpuid_level;
  1027. settings.acpi = (!infos.state && settings.acpi !== undefined) ? settings.acpi : infos.acpi;
  1028. settings.memory_size = (!infos.state && settings.memory_size) ? settings.memory_size : infos.memory_size;
  1029. settings.vga_memory_size = (!infos.state && settings.vga_memory_size) ? settings.vga_memory_size : infos.vga_memory_size;
  1030. settings.id = infos.id;
  1031. if(infos.boot_order !== undefined)
  1032. {
  1033. settings.boot_order = infos.boot_order;
  1034. }
  1035. let chunk_size = parseInt(query_args["chunk_size"], 10);
  1036. if(chunk_size >= 0)
  1037. {
  1038. if(chunk_size)
  1039. {
  1040. chunk_size = Math.min(4 * 1024 * 1024, Math.max(512, chunk_size));
  1041. chunk_size = 1 << Math.ceil(Math.log2(chunk_size));
  1042. }
  1043. else
  1044. {
  1045. chunk_size = undefined;
  1046. }
  1047. if(settings.hda)
  1048. {
  1049. settings.hda.fixed_chunk_size = chunk_size;
  1050. }
  1051. if(settings.cdrom)
  1052. {
  1053. settings.cdrom.fixed_chunk_size = chunk_size;
  1054. }
  1055. }
  1056. if(!DEBUG && infos.homepage)
  1057. {
  1058. $("description").style.display = "block";
  1059. const link = document.createElement("a");
  1060. link.href = infos.homepage;
  1061. link.textContent = infos.name;
  1062. link.target = "_blank";
  1063. $("description").appendChild(document.createTextNode("Running "));
  1064. $("description").appendChild(link);
  1065. }
  1066. start_emulation(settings, done);
  1067. }
  1068. function done(emulator)
  1069. {
  1070. if(query_args["c"])
  1071. {
  1072. setTimeout(function()
  1073. {
  1074. //emulator.serial0_send(query_args["c"] + "\n");
  1075. emulator.keyboard_send_text(query_args["c"] + "\n");
  1076. }, 25);
  1077. }
  1078. }
  1079. }
  1080. function debug_onload(settings)
  1081. {
  1082. // called on window.onload, in debug mode
  1083. var log_levels = $("log_levels");
  1084. if(log_levels)
  1085. {
  1086. for(var i = 0; i < LOG_NAMES.length; i++)
  1087. {
  1088. var mask = LOG_NAMES[i][0];
  1089. if(mask === 1)
  1090. continue;
  1091. var name = LOG_NAMES[i][1].toLowerCase(),
  1092. input = document.createElement("input"),
  1093. label = document.createElement("label");
  1094. input.type = "checkbox";
  1095. label.htmlFor = input.id = "log_" + name;
  1096. if(LOG_LEVEL & mask)
  1097. {
  1098. input.checked = true;
  1099. }
  1100. input.mask = mask;
  1101. label.appendChild(input);
  1102. label.appendChild(document.createTextNode(v86util.pads(name, 4) + " "));
  1103. log_levels.appendChild(label);
  1104. if(i === Math.floor(LOG_NAMES.length / 2))
  1105. {
  1106. log_levels.appendChild(document.createTextNode("\n"));
  1107. }
  1108. }
  1109. log_levels.onchange = function(e)
  1110. {
  1111. var target = e.target,
  1112. mask = target.mask;
  1113. if(target.checked)
  1114. {
  1115. LOG_LEVEL |= mask;
  1116. }
  1117. else
  1118. {
  1119. LOG_LEVEL &= ~mask;
  1120. }
  1121. target.blur();
  1122. };
  1123. }
  1124. }
  1125. window.addEventListener("load", onload, false);
  1126. // old webkit fires popstate on every load, fuck webkit
  1127. // https://code.google.com/p/chromium/issues/detail?id=63040
  1128. window.addEventListener("load", function()
  1129. {
  1130. setTimeout(function()
  1131. {
  1132. window.addEventListener("popstate", onpopstate);
  1133. }, 0);
  1134. });
  1135. // works in firefox and chromium
  1136. if(document.readyState === "complete")
  1137. {
  1138. onload();
  1139. }
  1140. /** @param {?=} done */
  1141. function start_emulation(settings, done)
  1142. {
  1143. /** @const */
  1144. var MB = 1024 * 1024;
  1145. var memory_size = settings.memory_size;
  1146. if(!memory_size)
  1147. {
  1148. memory_size = parseInt($("memory_size").value, 10) * MB;
  1149. if(!memory_size)
  1150. {
  1151. alert("Invalid memory size - reset to 128MB");
  1152. memory_size = 128 * MB;
  1153. }
  1154. }
  1155. var vga_memory_size = settings.vga_memory_size;
  1156. if(!vga_memory_size)
  1157. {
  1158. vga_memory_size = parseInt($("video_memory_size").value, 10) * MB;
  1159. if(!vga_memory_size)
  1160. {
  1161. alert("Invalid video memory size - reset to 8MB");
  1162. vga_memory_size = 8 * MB;
  1163. }
  1164. }
  1165. if(!settings.fda)
  1166. {
  1167. var floppy_file = $("floppy_image").files[0];
  1168. if(floppy_file)
  1169. {
  1170. settings.fda = { buffer: floppy_file };
  1171. }
  1172. }
  1173. if(!settings.bzimage)
  1174. {
  1175. var bzimage = $("bzimage").files[0];
  1176. if(bzimage)
  1177. {
  1178. settings.bzimage = { buffer: bzimage };
  1179. }
  1180. }
  1181. if(!settings.initrd)
  1182. {
  1183. var initrd = $("initrd").files[0];
  1184. if(initrd)
  1185. {
  1186. settings.initrd = { buffer: initrd };
  1187. }
  1188. }
  1189. const networking_proxy = settings.networking_proxy === undefined ? $("networking_proxy").value : settings.networking_proxy;
  1190. const disable_audio = settings.audio === undefined ? $("disable_audio").checked : !settings.audio;
  1191. const enable_acpi = settings.acpi === undefined ? $("enable_acpi").checked : settings.acpi;
  1192. /** @const */
  1193. var BIOSPATH = "bios/";
  1194. if(settings.use_bochs_bios)
  1195. {
  1196. var biosfile = "bochs-bios.bin";
  1197. var vgabiosfile = "bochs-vgabios.bin";
  1198. }
  1199. else
  1200. {
  1201. var biosfile = DEBUG ? "seabios-debug.bin" : "seabios.bin";
  1202. var vgabiosfile = DEBUG ? "vgabios-debug.bin" : "vgabios.bin";
  1203. }
  1204. var bios;
  1205. var vga_bios;
  1206. // a bios is only needed if the machine is booted
  1207. if(!settings.initial_state)
  1208. {
  1209. bios = {
  1210. url: BIOSPATH + biosfile,
  1211. };
  1212. vga_bios = {
  1213. url: BIOSPATH + vgabiosfile,
  1214. };
  1215. }
  1216. var emulator = new V86Starter({
  1217. "memory_size": memory_size,
  1218. "vga_memory_size": vga_memory_size,
  1219. "screen_container": $("screen_container"),
  1220. "serial_container_xtermjs": $("terminal"),
  1221. "boot_order": settings.boot_order || parseInt($("boot_order").value, 16) || 0,
  1222. "network_relay_url": ON_LOCALHOST ? "ws://localhost:8080/" : networking_proxy,
  1223. "bios": bios,
  1224. "vga_bios": vga_bios,
  1225. "fda": settings.fda,
  1226. "hda": settings.hda,
  1227. "hdb": settings.hdb,
  1228. "cdrom": settings.cdrom,
  1229. "multiboot": settings.multiboot,
  1230. "bzimage": settings.bzimage,
  1231. "initrd": settings.initrd,
  1232. "cmdline": settings.cmdline,
  1233. "bzimage_initrd_from_filesystem": settings.bzimage_initrd_from_filesystem,
  1234. "acpi": enable_acpi,
  1235. "initial_state": settings.initial_state,
  1236. "filesystem": settings.filesystem || {},
  1237. "disable_speaker": disable_audio,
  1238. "mac_address_translation": settings.mac_address_translation,
  1239. "cpuid_level": settings.cpuid_level,
  1240. "autostart": true,
  1241. });
  1242. if(DEBUG) window["emulator"] = emulator;
  1243. emulator.add_listener("emulator-ready", function()
  1244. {
  1245. if(DEBUG)
  1246. {
  1247. debug_start(emulator);
  1248. }
  1249. if(emulator.v86.cpu.wm.exports["profiler_is_enabled"]())
  1250. {
  1251. const CLEAR_STATS = false;
  1252. var panel = document.createElement("pre");
  1253. document.body.appendChild(panel);
  1254. setInterval(function()
  1255. {
  1256. if(!emulator.is_running())
  1257. {
  1258. return;
  1259. }
  1260. const text = print_stats.stats_to_string(emulator.v86.cpu);
  1261. panel.textContent = text;
  1262. CLEAR_STATS && emulator.v86.cpu.clear_opstats();
  1263. }, CLEAR_STATS ? 5000 : 1000);
  1264. }
  1265. if(settings.id === "dsl" || settings.id === "helenos")
  1266. {
  1267. setTimeout(() => {
  1268. // hack: Start automatically
  1269. emulator.keyboard_send_text("\n");
  1270. }, 3000);
  1271. }
  1272. else if(settings.id === "android" || settings.id === "android4")
  1273. {
  1274. setTimeout(() => {
  1275. // hack: select vesa mode and start automatically
  1276. emulator.keyboard_send_scancodes([0xe050, 0xe050 | 0x80]);
  1277. emulator.keyboard_send_text("\n");
  1278. }, 3000);
  1279. }
  1280. init_ui(settings, emulator);
  1281. done && done(emulator);
  1282. });
  1283. emulator.add_listener("download-progress", function(e)
  1284. {
  1285. show_progress(e);
  1286. });
  1287. emulator.add_listener("download-error", function(e)
  1288. {
  1289. var el = $("loading");
  1290. el.style.display = "block";
  1291. el.textContent = "Loading " + e.file_name + " failed. Check your connection " +
  1292. "and reload the page to try again.";
  1293. });
  1294. }
  1295. /**
  1296. * @param {Object} settings
  1297. * @param {V86Starter} emulator
  1298. */
  1299. function init_ui(settings, emulator)
  1300. {
  1301. $("boot_options").style.display = "none";
  1302. $("loading").style.display = "none";
  1303. $("runtime_options").style.display = "block";
  1304. $("runtime_infos").style.display = "block";
  1305. $("screen_container").style.display = "block";
  1306. if(settings.filesystem)
  1307. {
  1308. init_filesystem_panel(emulator);
  1309. }
  1310. else
  1311. {
  1312. emulator.add_listener("9p-attach", function()
  1313. {
  1314. init_filesystem_panel(emulator);
  1315. });
  1316. }
  1317. $("run").onclick = function()
  1318. {
  1319. if(emulator.is_running())
  1320. {
  1321. $("run").value = "Run";
  1322. emulator.stop();
  1323. }
  1324. else
  1325. {
  1326. $("run").value = "Pause";
  1327. emulator.run();
  1328. }
  1329. $("run").blur();
  1330. };
  1331. $("exit").onclick = function()
  1332. {
  1333. emulator.stop();
  1334. location.href = location.pathname;
  1335. };
  1336. $("lock_mouse").onclick = function()
  1337. {
  1338. if(!mouse_is_enabled)
  1339. {
  1340. $("toggle_mouse").onclick();
  1341. }
  1342. emulator.lock_mouse();
  1343. $("lock_mouse").blur();
  1344. };
  1345. var mouse_is_enabled = true;
  1346. $("toggle_mouse").onclick = function()
  1347. {
  1348. mouse_is_enabled = !mouse_is_enabled;
  1349. emulator.mouse_set_status(mouse_is_enabled);
  1350. $("toggle_mouse").value = (mouse_is_enabled ? "Dis" : "En") + "able mouse";
  1351. $("toggle_mouse").blur();
  1352. };
  1353. var last_tick = 0;
  1354. var running_time = 0;
  1355. var last_instr_counter = 0;
  1356. var interval = null;
  1357. var os_uses_mouse = false;
  1358. var total_instructions = 0;
  1359. function update_info()
  1360. {
  1361. var now = Date.now();
  1362. var instruction_counter = emulator.get_instruction_counter();
  1363. if(instruction_counter < last_instr_counter)
  1364. {
  1365. // 32-bit wrap-around
  1366. last_instr_counter -= 0x100000000;
  1367. }
  1368. var last_ips = instruction_counter - last_instr_counter;
  1369. last_instr_counter = instruction_counter;
  1370. total_instructions += last_ips;
  1371. var delta_time = now - last_tick;
  1372. if(delta_time)
  1373. {
  1374. running_time += delta_time;
  1375. last_tick = now;
  1376. $("speed").textContent = (last_ips / 1000 / delta_time).toFixed(1);
  1377. $("avg_speed").textContent = (total_instructions / 1000 / running_time).toFixed(1);
  1378. $("running_time").textContent = format_timestamp(running_time / 1000 | 0);
  1379. }
  1380. }
  1381. emulator.add_listener("emulator-started", function()
  1382. {
  1383. last_tick = Date.now();
  1384. interval = setInterval(update_info, 1000);
  1385. });
  1386. emulator.add_listener("emulator-stopped", function()
  1387. {
  1388. update_info();
  1389. if(interval !== null)
  1390. {
  1391. clearInterval(interval);
  1392. }
  1393. });
  1394. var stats_9p = {
  1395. read: 0,
  1396. write: 0,
  1397. files: [],
  1398. };
  1399. emulator.add_listener("9p-read-start", function(args)
  1400. {
  1401. const file = args[0];
  1402. stats_9p.files.push(file);
  1403. $("info_filesystem").style.display = "block";
  1404. $("info_filesystem_status").textContent = "Loading ...";
  1405. $("info_filesystem_last_file").textContent = file;
  1406. });
  1407. emulator.add_listener("9p-read-end", function(args)
  1408. {
  1409. stats_9p.read += args[1];
  1410. $("info_filesystem_bytes_read").textContent = stats_9p.read;
  1411. const file = args[0];
  1412. stats_9p.files = stats_9p.files.filter(f => f !== file);
  1413. if(stats_9p.files[0])
  1414. {
  1415. $("info_filesystem_last_file").textContent = stats_9p.files[0];
  1416. }
  1417. else
  1418. {
  1419. $("info_filesystem_status").textContent = "Idle";
  1420. }
  1421. });
  1422. emulator.add_listener("9p-write-end", function(args)
  1423. {
  1424. stats_9p.write += args[1];
  1425. $("info_filesystem_bytes_written").textContent = stats_9p.write;
  1426. if(!stats_9p.files[0])
  1427. {
  1428. $("info_filesystem_last_file").textContent = args[0];
  1429. }
  1430. });
  1431. var stats_storage = {
  1432. read: 0,
  1433. read_sectors: 0,
  1434. write: 0,
  1435. write_sectors: 0,
  1436. };
  1437. emulator.add_listener("ide-read-start", function()
  1438. {
  1439. $("info_storage").style.display = "block";
  1440. $("info_storage_status").textContent = "Loading ...";
  1441. });
  1442. emulator.add_listener("ide-read-end", function(args)
  1443. {
  1444. stats_storage.read += args[1];
  1445. stats_storage.read_sectors += args[2];
  1446. $("info_storage_status").textContent = "Idle";
  1447. $("info_storage_bytes_read").textContent = stats_storage.read;
  1448. $("info_storage_sectors_read").textContent = stats_storage.read_sectors;
  1449. });
  1450. emulator.add_listener("ide-write-end", function(args)
  1451. {
  1452. stats_storage.write += args[1];
  1453. stats_storage.write_sectors += args[2];
  1454. $("info_storage_bytes_written").textContent = stats_storage.write;
  1455. $("info_storage_sectors_written").textContent = stats_storage.write_sectors;
  1456. });
  1457. var stats_net = {
  1458. bytes_transmitted: 0,
  1459. bytes_received: 0,
  1460. };
  1461. emulator.add_listener("eth-receive-end", function(args)
  1462. {
  1463. stats_net.bytes_received += args[0];
  1464. $("info_network").style.display = "block";
  1465. $("info_network_bytes_received").textContent = stats_net.bytes_received;
  1466. });
  1467. emulator.add_listener("eth-transmit-end", function(args)
  1468. {
  1469. stats_net.bytes_transmitted += args[0];
  1470. $("info_network").style.display = "block";
  1471. $("info_network_bytes_transmitted").textContent = stats_net.bytes_transmitted;
  1472. });
  1473. emulator.add_listener("mouse-enable", function(is_enabled)
  1474. {
  1475. os_uses_mouse = is_enabled;
  1476. $("info_mouse_enabled").textContent = is_enabled ? "Yes" : "No";
  1477. });
  1478. emulator.add_listener("screen-set-mode", function(is_graphical)
  1479. {
  1480. if(is_graphical)
  1481. {
  1482. $("info_vga_mode").textContent = "Graphical";
  1483. }
  1484. else
  1485. {
  1486. $("info_vga_mode").textContent = "Text";
  1487. $("info_res").textContent = "-";
  1488. $("info_bpp").textContent = "-";
  1489. }
  1490. });
  1491. emulator.add_listener("screen-set-size-graphical", function(args)
  1492. {
  1493. $("info_res").textContent = args[0] + "x" + args[1];
  1494. $("info_bpp").textContent = args[4];
  1495. });
  1496. $("reset").onclick = function()
  1497. {
  1498. emulator.restart();
  1499. $("reset").blur();
  1500. };
  1501. add_image_download_button(settings.hda, "hda");
  1502. add_image_download_button(settings.hdb, "hdb");
  1503. add_image_download_button(settings.fda, "fda");
  1504. add_image_download_button(settings.fdb, "fdb");
  1505. add_image_download_button(settings.cdrom, "cdrom");
  1506. function add_image_download_button(obj, type)
  1507. {
  1508. var elem = $("get_" + type + "_image");
  1509. if(!obj || obj.size > 100 * 1024 * 1024)
  1510. {
  1511. elem.style.display = "none";
  1512. return;
  1513. }
  1514. elem.onclick = function(e)
  1515. {
  1516. let buffer = emulator.disk_images[type];
  1517. let filename = buffer.file && buffer.file.name || (settings.id + (type === "cdrom" ? ".iso" : ".img"));
  1518. if(buffer.get_as_file)
  1519. {
  1520. var file = buffer.get_as_file(filename);
  1521. download(file, filename);
  1522. }
  1523. else
  1524. {
  1525. buffer.get_buffer(function(b)
  1526. {
  1527. if(b)
  1528. {
  1529. dump_file(b, filename);
  1530. }
  1531. else
  1532. {
  1533. alert("The file could not be loaded. Maybe it's too big?");
  1534. }
  1535. });
  1536. }
  1537. elem.blur();
  1538. };
  1539. }
  1540. $("memory_dump").onclick = function()
  1541. {
  1542. const mem8 = emulator.v86.cpu.mem8;
  1543. dump_file(new Uint8Array(mem8.buffer, mem8.byteOffset, mem8.length), "v86memory.bin");
  1544. $("memory_dump").blur();
  1545. };
  1546. //$("memory_dump_dmp").onclick = function()
  1547. //{
  1548. // var memory = emulator.v86.cpu.mem8;
  1549. // var memory_size = memory.length;
  1550. // var page_size = 4096;
  1551. // var header = new Uint8Array(4096);
  1552. // var header32 = new Int32Array(header.buffer);
  1553. // header32[0] = 0x45474150; // 'PAGE'
  1554. // header32[1] = 0x504D5544; // 'DUMP'
  1555. // header32[0x10 >> 2] = emulator.v86.cpu.cr[3]; // DirectoryTableBase
  1556. // header32[0x24 >> 2] = 1; // NumberProcessors
  1557. // header32[0xf88 >> 2] = 1; // DumpType: full dump
  1558. // header32[0xfa0 >> 2] = header.length + memory_size; // RequiredDumpSpace
  1559. // header32[0x064 + 0 >> 2] = 1; // NumberOfRuns
  1560. // header32[0x064 + 4 >> 2] = memory_size / page_size; // NumberOfPages
  1561. // header32[0x064 + 8 >> 2] = 0; // BasePage
  1562. // header32[0x064 + 12 >> 2] = memory_size / page_size; // PageCount
  1563. // dump_file([header, memory], "v86memory.dmp");
  1564. // $("memory_dump_dmp").blur();
  1565. //};
  1566. $("capture_network_traffic").onclick = function()
  1567. {
  1568. this.value = "0 packets";
  1569. let capture = [];
  1570. function do_capture(direction, data)
  1571. {
  1572. capture.push({ direction, time: performance.now() / 1000, hex_dump: hex_dump(data) });
  1573. $("capture_network_traffic").value = capture.length + " packets";
  1574. }
  1575. emulator.emulator_bus.register("net0-receive", do_capture.bind(this, "I"));
  1576. emulator.add_listener("net0-send", do_capture.bind(this, "O"));
  1577. this.onclick = function()
  1578. {
  1579. const capture_raw = capture.map(({ direction, time, hex_dump }) => {
  1580. // https://www.wireshark.org/docs/wsug_html_chunked/ChIOImportSection.html
  1581. // In wireshark: file -> import from hex -> tick direction indication, timestamp %s.%f
  1582. return direction + " " + time.toFixed(6) + hex_dump + "\n";
  1583. }).join("");
  1584. dump_file(capture_raw, "traffic.hex");
  1585. capture = [];
  1586. this.value = "0 packets";
  1587. };
  1588. };
  1589. $("save_state").onclick = async function()
  1590. {
  1591. const result = await emulator.save_state();
  1592. dump_file(result, "v86state.bin");
  1593. $("save_state").blur();
  1594. };
  1595. $("load_state").onclick = function()
  1596. {
  1597. $("load_state_input").click();
  1598. $("load_state").blur();
  1599. };
  1600. $("load_state_input").onchange = async function()
  1601. {
  1602. var file = this.files[0];
  1603. if(!file)
  1604. {
  1605. return;
  1606. }
  1607. var was_running = emulator.is_running();
  1608. if(was_running)
  1609. {
  1610. await emulator.stop();
  1611. }
  1612. var filereader = new FileReader();
  1613. filereader.onload = async function(e)
  1614. {
  1615. try
  1616. {
  1617. await emulator.restore_state(e.target.result);
  1618. }
  1619. catch(err)
  1620. {
  1621. alert("Something bad happened while restoring the state:\n" + err + "\n\n" +
  1622. "Note that the current configuration must be the same as the original");
  1623. throw err;
  1624. }
  1625. if(was_running)
  1626. {
  1627. emulator.run();
  1628. }
  1629. };
  1630. filereader.readAsArrayBuffer(file);
  1631. this.value = "";
  1632. };
  1633. $("ctrlaltdel").onclick = function()
  1634. {
  1635. emulator.keyboard_send_scancodes([
  1636. 0x1D, // ctrl
  1637. 0x38, // alt
  1638. 0x53, // delete
  1639. // break codes
  1640. 0x1D | 0x80,
  1641. 0x38 | 0x80,
  1642. 0x53 | 0x80,
  1643. ]);
  1644. $("ctrlaltdel").blur();
  1645. };
  1646. $("alttab").onclick = function()
  1647. {
  1648. emulator.keyboard_send_scancodes([
  1649. 0x38, // alt
  1650. 0x0F, // tab
  1651. ]);
  1652. setTimeout(function()
  1653. {
  1654. emulator.keyboard_send_scancodes([
  1655. 0x38 | 0x80,
  1656. 0x0F | 0x80,
  1657. ]);
  1658. }, 100);
  1659. $("alttab").blur();
  1660. };
  1661. $("scale").onchange = function()
  1662. {
  1663. var n = parseFloat(this.value);
  1664. if(n || n > 0)
  1665. {
  1666. emulator.screen_set_scale(n, n);
  1667. }
  1668. };
  1669. $("fullscreen").onclick = function()
  1670. {
  1671. emulator.screen_go_fullscreen();
  1672. };
  1673. $("screen_container").onclick = function()
  1674. {
  1675. if(mouse_is_enabled && os_uses_mouse)
  1676. {
  1677. emulator.lock_mouse();
  1678. $("lock_mouse").blur();
  1679. }
  1680. else
  1681. {
  1682. // allow text selection
  1683. if(window.getSelection().isCollapsed)
  1684. {
  1685. let phone_keyboard = document.getElementsByClassName("phone_keyboard")[0];
  1686. // stop mobile browser from scrolling into view when the keyboard is shown
  1687. phone_keyboard.style.top = document.body.scrollTop + 100 + "px";
  1688. phone_keyboard.style.left = document.body.scrollLeft + 100 + "px";
  1689. phone_keyboard.focus();
  1690. }
  1691. }
  1692. };
  1693. const phone_keyboard = document.getElementsByClassName("phone_keyboard")[0];
  1694. phone_keyboard.setAttribute("autocorrect", "off");
  1695. phone_keyboard.setAttribute("autocapitalize", "off");
  1696. phone_keyboard.setAttribute("spellcheck", "false");
  1697. phone_keyboard.tabIndex = 0;
  1698. $("screen_container").addEventListener("mousedown", e =>
  1699. {
  1700. phone_keyboard.focus();
  1701. }, false);
  1702. $("take_screenshot").onclick = function()
  1703. {
  1704. emulator.screen_make_screenshot();
  1705. $("take_screenshot").blur();
  1706. };
  1707. if(emulator.speaker_adapter)
  1708. {
  1709. let is_muted = false;
  1710. $("mute").onclick = function()
  1711. {
  1712. if(is_muted)
  1713. {
  1714. emulator.speaker_adapter.mixer.set_volume(1, undefined);
  1715. is_muted = false;
  1716. $("mute").value = "Mute";
  1717. }
  1718. else
  1719. {
  1720. emulator.speaker_adapter.mixer.set_volume(0, undefined);
  1721. is_muted = true;
  1722. $("mute").value = "Unmute";
  1723. }
  1724. $("mute").blur();
  1725. };
  1726. }
  1727. else
  1728. {
  1729. $("mute").remove();
  1730. }
  1731. window.addEventListener("keydown", ctrl_w_rescue, false);
  1732. window.addEventListener("keyup", ctrl_w_rescue, false);
  1733. window.addEventListener("blur", ctrl_w_rescue, false);
  1734. function ctrl_w_rescue(e)
  1735. {
  1736. if(e.ctrlKey)
  1737. {
  1738. window.onbeforeunload = function()
  1739. {
  1740. window.onbeforeunload = null;
  1741. return "CTRL-W cannot be sent to the emulator.";
  1742. };
  1743. }
  1744. else
  1745. {
  1746. window.onbeforeunload = null;
  1747. }
  1748. }
  1749. }
  1750. function init_filesystem_panel(emulator)
  1751. {
  1752. $("filesystem_panel").style.display = "block";
  1753. $("filesystem_send_file").onchange = function()
  1754. {
  1755. Array.prototype.forEach.call(this.files, function(file)
  1756. {
  1757. var loader = new v86util.SyncFileBuffer(file);
  1758. loader.onload = function()
  1759. {
  1760. loader.get_buffer(async function(buffer)
  1761. {
  1762. await emulator.create_file("/" + file.name, new Uint8Array(buffer));
  1763. });
  1764. };
  1765. loader.load();
  1766. }, this);
  1767. this.value = "";
  1768. this.blur();
  1769. };
  1770. $("filesystem_get_file").onkeypress = async function(e)
  1771. {
  1772. if(e.which !== 13)
  1773. {
  1774. return;
  1775. }
  1776. this.disabled = true;
  1777. let result;
  1778. try
  1779. {
  1780. result = await emulator.read_file(this.value);
  1781. }
  1782. catch(err)
  1783. {
  1784. console.log(err);
  1785. }
  1786. this.disabled = false;
  1787. if(result)
  1788. {
  1789. var filename = this.value.replace(/\/$/, "").split("/");
  1790. filename = filename[filename.length - 1] || "root";
  1791. dump_file(result, filename);
  1792. this.value = "";
  1793. }
  1794. else
  1795. {
  1796. alert("Can't read file");
  1797. }
  1798. };
  1799. }
  1800. function debug_start(emulator)
  1801. {
  1802. if(!emulator.v86)
  1803. {
  1804. return;
  1805. }
  1806. // called as soon as soon as emulation is started, in debug mode
  1807. var debug = emulator.v86.cpu.debug;
  1808. $("dump_gdt").onclick = debug.dump_gdt_ldt.bind(debug);
  1809. $("dump_idt").onclick = debug.dump_idt.bind(debug);
  1810. $("dump_regs").onclick = debug.dump_regs.bind(debug);
  1811. $("dump_pt").onclick = debug.dump_page_structures.bind(debug);
  1812. $("dump_log").onclick = function()
  1813. {
  1814. dump_file(log_data.join(""), "v86.log");
  1815. };
  1816. var cpu = emulator.v86.cpu;
  1817. $("debug_panel").style.display = "block";
  1818. setInterval(function()
  1819. {
  1820. $("debug_panel").textContent =
  1821. cpu.debug.get_regs_short().join("\n") + "\n" + cpu.debug.get_state();
  1822. $("dump_log").value = "Dump log" + (log_data.length ? " (" + log_data.length + " lines)" : "");
  1823. }, 1000);
  1824. // helps debugging
  1825. window.emulator = emulator;
  1826. window.cpu = cpu;
  1827. window.dump_file = dump_file;
  1828. }
  1829. function onpopstate(e)
  1830. {
  1831. location.reload();
  1832. }
  1833. function set_profile(prof)
  1834. {
  1835. if(window.history.pushState)
  1836. {
  1837. window.history.pushState({ profile: prof }, "", "?profile=" + prof);
  1838. }
  1839. }
  1840. })();