main.js 50 KB

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