Spaces:
No application file
No application file
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Virtual x86</title> | |
| <meta name="viewport" content="width=device-width,initial-scale=1.0"> | |
| <link rel="stylesheet" href="v86.css"> | |
| <!-- keep your original v86 bundle path unchanged --> | |
| <script src="build/v86_all.js"></script> | |
| </head> | |
| <body style="font-family: Arial, sans-serif; background:#111; color:#eee;"> | |
| <div> | |
| <div id="boot_options"> | |
| <h2 style="margin:0 0 8px 0">Virtual x86 - Boot Options</h2> | |
| <hr> | |
| <table> | |
| <tr> | |
| <td width="250">CD image</td> | |
| <td> | |
| <input type="file" id="cd_image"><br> | |
| <input type="text" id="cd_url" placeholder="Or paste ISO/IMG URL (http(s)://...) or path" style="width:90%; margin-top:6px;"><br> | |
| <div id="cd_display" style="font-size:12px; color:#bbb; margin-top:4px;">No file selected</div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td>Floppy disk image</td> | |
| <td> | |
| <input type="file" id="floppy_image"><br> | |
| <input type="text" id="floppy_url" placeholder="Or paste floppy URL/path" style="width:90%; margin-top:6px;"><br> | |
| <div id="floppy_display" style="font-size:12px; color:#bbb; margin-top:4px;">No file selected</div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td>Master Hard drive disk image</td> | |
| <td> | |
| <input type="file" id="hda_image"><br> | |
| <input type="text" id="hda_url" placeholder="Or paste HDD URL/path" style="width:90%; margin-top:6px;"><br> | |
| <div id="hda_display" style="font-size:12px; color:#bbb; margin-top:4px;">No file selected</div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td>Slave Hard drive disk image</td> | |
| <td> | |
| <input type="file" id="hdb_image"><br> | |
| <input type="text" id="hdb_url" placeholder="Or paste second HDD URL/path" style="width:90%; margin-top:6px;"><br> | |
| <div id="hdb_display" style="font-size:12px; color:#bbb; margin-top:4px;">No file selected</div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td>Multiboot kernel image</td> | |
| <td> | |
| <input type="file" id="multiboot_image"><br> | |
| <input type="text" id="multiboot_url" placeholder="Or paste kernel image URL/path" style="width:90%; margin-top:6px;"><br> | |
| <div id="multiboot_display" style="font-size:12px; color:#bbb; margin-top:4px;">No file selected</div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td colspan="2"><hr></td> | |
| </tr> | |
| <tr> | |
| <td>Memory size</td> | |
| <td> | |
| <input id="memory_size" type="number" value="256" min="16" max="4096" step="16"> MB<br> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td>Video Memory size</td> | |
| <td> | |
| <input id="video_memory_size" type="number" value="8" min="1" max="128" step="1"> MB<br> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td colspan="2"><hr></td> | |
| </tr> | |
| <tr> | |
| <td>Boot order</td> | |
| <td> | |
| <select id="boot_order"> | |
| <option value="213">CD / Floppy / Hard Disk</option> | |
| <option value="123">CD / Hard Disk / Floppy</option> | |
| <option value="231">Floppy / CD / Hard Disk</option> | |
| <option value="321">Floppy / Hard Disk / CD</option> | |
| <option value="312">Hard Disk / Floppy / CD</option> | |
| <option value="132">Hard Disk / CD / Floppy</option> | |
| </select> | |
| </td> | |
| </tr> | |
| </table> | |
| <br> | |
| <button id="start_emulation" style="padding:8px 14px; font-size:16px;">Start Emulation</button> | |
| <div id="status" style="margin-top:10px; font-size:13px; color:#cfc;"></div> | |
| <br><br> | |
| </div> | |
| <div id="runtime_options" style="display: none; margin-bottom:8px;"> | |
| <input type="button" id="pause_btn" value="Pause"> | |
| <input type="button" id="reset_btn" value="Reset"> | |
| <input type="button" id="exit_btn" value="Exit"> | |
| <input type="button" id="send_cad" value="Send Ctrl-Alt-Del"> | |
| </div> | |
| <pre style="margin: 0" id="log_levels"></pre> | |
| <pre style="margin: 3px 0 0 0" id="debug_infos"></pre> | |
| </div> | |
| <!-- Screen container (keeps original structure) --> | |
| <div id="screen_container" style="display: none; position: relative;"> | |
| <div id="screen"></div> | |
| <canvas id="vga"></canvas> | |
| <div style="position: absolute; top: 0; z-index: 10"> | |
| <textarea class="phone_keyboard"></textarea> | |
| </div> | |
| </div> | |
| <textarea readonly id="log" style="display:none"></textarea> | |
| <textarea spellcheck="false" cols="40" rows="12" id="serial" style="display:none"></textarea> | |
| <script> | |
| /* | |
| IMPORTANT: | |
| - This script reads selected local files into ArrayBuffers and passes them to v86. | |
| - Browsers do NOT reveal real absolute local paths; the script shows file.name or the browser's fakepath. | |
| - Keep your folders (build/, bios/) unchanged as requested. | |
| */ | |
| (function() { | |
| // Helper: read File -> ArrayBuffer | |
| function readFileAsArrayBuffer(file) { | |
| return new Promise(function(resolve, reject) { | |
| var fr = new FileReader(); | |
| fr.onload = function() { resolve(fr.result); }; | |
| fr.onerror = function(e) { reject(e); }; | |
| fr.readAsArrayBuffer(file); | |
| }); | |
| } | |
| // Drives mapping | |
| var drives = [ | |
| { fileId: "cd_image", textId: "cd_url", displayId: "cd_display", optionName: "cdrom" }, | |
| { fileId: "floppy_image", textId: "floppy_url", displayId: "floppy_display", optionName: "fda" }, | |
| { fileId: "hda_image", textId: "hda_url", displayId: "hda_display", optionName: "hda" }, | |
| { fileId: "hdb_image", textId: "hdb_url", displayId: "hdb_display", optionName: "hdb" }, | |
| { fileId: "multiboot_image", textId: "multiboot_url", displayId: "multiboot_display", optionName: "multiboot" } | |
| ]; | |
| // Attach change handlers: autofill text and update display | |
| drives.forEach(function(d) { | |
| var fileInput = document.getElementById(d.fileId); | |
| var textInput = document.getElementById(d.textId); | |
| var display = document.getElementById(d.displayId); | |
| if (!fileInput || !textInput || !display) return; | |
| fileInput.addEventListener("change", function() { | |
| if (fileInput.files && fileInput.files.length > 0) { | |
| var f = fileInput.files[0]; | |
| // Browsers restrict absolute path; we show either browser-provided fake path or filename | |
| // some browsers set fileInput.value to "C:\fakepath\filename.ext" | |
| var shown = fileInput.value && fileInput.value.indexOf("fakepath") > -1 ? fileInput.value : f.name; | |
| textInput.value = shown; | |
| display.textContent = "Selected: " + shown; | |
| } else { | |
| textInput.value = ""; | |
| display.textContent = "No file selected"; | |
| } | |
| }); | |
| textInput.addEventListener("input", function() { | |
| display.textContent = this.value ? ("Path/URL: " + this.value) : "No file selected"; | |
| }); | |
| }); | |
| // Wait for v86 to be available | |
| function v86Available() { | |
| return (typeof V86Starter === "function" || typeof window.V86Starter === "function"); | |
| } | |
| // Start emulator click | |
| document.getElementById("start_emulation").addEventListener("click", async function() { | |
| var status = document.getElementById("status"); | |
| status.textContent = ""; | |
| if (!v86Available()) { | |
| status.style.color = "#f88"; | |
| status.textContent = "Error: v86 library not loaded - check build/v86_all.js is present."; | |
| return; | |
| } | |
| // gather config | |
| var memorySizeMb = parseInt(document.getElementById("memory_size").value, 10) || 256; | |
| var videoMemoryMb = parseInt(document.getElementById("video_memory_size").value, 10) || 8; | |
| var bootOrder = parseInt(document.getElementById("boot_order").value, 10) || 213; | |
| // base options (keep your bios paths exactly) | |
| var options = { | |
| wasm_path: "build/v86.wasm", | |
| memory_size: memorySizeMb * 1024 * 1024, | |
| vga_memory_size: videoMemoryMb * 1024 * 1024, | |
| screen_container: document.getElementById("screen_container"), | |
| bios: { url: "bios/seabios.bin" }, | |
| vga_bios: { url: "bios/vgabios.bin" }, | |
| boot_order: bootOrder, | |
| autostart: true | |
| }; | |
| // read files if selected (async). If file chosen -> use buffer, else if text is a URL -> use url | |
| try { | |
| for (var i = 0; i < drives.length; i++) { | |
| var d = drives[i]; | |
| var fileEl = document.getElementById(d.fileId); | |
| var textEl = document.getElementById(d.textId); | |
| var val = (textEl && textEl.value) ? textEl.value.trim() : ""; | |
| if (fileEl && fileEl.files && fileEl.files.length > 0) { | |
| // read file to ArrayBuffer | |
| status.style.color = "#ffc"; | |
| status.textContent = "Reading " + fileEl.files[0].name + " ..."; | |
| var buf = await readFileAsArrayBuffer(fileEl.files[0]); | |
| // pass ArrayBuffer directly to v86 | |
| options[d.optionName] = { buffer: buf }; | |
| } else if (val) { | |
| // If user typed a URL (http/https) we provide it as url, otherwise still pass as url (user knows their path) | |
| options[d.optionName] = { url: val }; | |
| } | |
| } | |
| } catch (err) { | |
| status.style.color = "#f88"; | |
| status.textContent = "Failed reading image file: " + (err && err.message ? err.message : err); | |
| return; | |
| } | |
| // hide boot UI and show screen container + runtime controls | |
| document.getElementById("boot_options").style.display = "none"; | |
| document.getElementById("screen_container").style.display = "block"; | |
| document.getElementById("runtime_options").style.display = "block"; | |
| status.style.color = "#cfc"; | |
| status.textContent = "Starting emulator... (this may take a few seconds)"; | |
| try { | |
| // start v86 | |
| window.emulator = new V86Starter(options); | |
| // wire up runtime buttons safely if emulator supports methods | |
| var pauseBtn = document.getElementById("pause_btn"); | |
| var resetBtn = document.getElementById("reset_btn"); | |
| var exitBtn = document.getElementById("exit_btn"); | |
| var cadBtn = document.getElementById("send_cad"); | |
| if (pauseBtn) { | |
| pauseBtn.addEventListener("click", function() { | |
| try { | |
| if (window.emulator && window.emulator.stop) { | |
| window.emulator.stop(); | |
| } | |
| } catch(e) { console.warn(e); } | |
| }); | |
| } | |
| if (resetBtn) { | |
| resetBtn.addEventListener("click", function() { | |
| try { window.emulator.reset(); } catch(e){ console.warn(e); } | |
| }); | |
| } | |
| if (exitBtn) { | |
| exitBtn.addEventListener("click", function() { | |
| try { window.emulator.stop(); } catch(e){ console.warn(e); } | |
| // show boot options again | |
| document.getElementById("boot_options").style.display = "block"; | |
| document.getElementById("runtime_options").style.display = "none"; | |
| document.getElementById("screen_container").style.display = "none"; | |
| }); | |
| } | |
| if (cadBtn) { | |
| cadBtn.addEventListener("click", function() { | |
| try { window.emulator.keyboard_send_ctrl_alt_del(); } catch(e){ | |
| // fallback attempts | |
| try { window.emulator.send_ctrl_alt_del(); } catch(_) { console.warn("send CtrlAltDel not supported"); } | |
| } | |
| }); | |
| } | |
| status.style.color = "#cfc"; | |
| status.textContent = "Emulator started."; | |
| } catch (e) { | |
| status.style.color = "#f88"; | |
| status.textContent = "Failed to start emulator: " + (e && e.message ? e.message : e); | |
| console.error(e); | |
| } | |
| }); | |
| })(); | |
| </script> | |
| </body> | |
| </html> |