<NME> main.js
<BEF> "use strict";

(function()
{
    /** @const */
    var ON_LOCALHOST = !location.hostname.endsWith("copy.sh");

    /**
     * @return {Object.<string, string>}
     */
    function get_query_arguments()
    {
        var query = location.search.substr(1).split("&");
        var parameters = {};

        for(var i = 0; i < query.length; i++)
        {
            var param = query[i].split("=");
            parameters[param[0]] = decodeURIComponent(param.slice(1).join("="));
        }

        return parameters;
    }

    function set_title(text)
    {
        document.title = text + " - Virtual x86" +  (DEBUG ? " - debug" : "");
        const description = document.querySelector("meta[name=description]");
        description && (description.content = "Running " + text);
    }

    function format_timestamp(time)
    {
        if(time < 60)
        {
            return time + "s";
        }
        else if(time < 3600)
        {
            return (time / 60 | 0) + "m " + v86util.pad0(time % 60, 2) + "s";
        }
        else
        {
            return (time / 3600 | 0) + "h " +
                v86util.pad0((time / 60 | 0) % 60, 2) + "m " +
                v86util.pad0(time % 60, 2) + "s";
        }
    }

    var progress_ticks = 0;

    function show_progress(e)
    {
        var el = $("loading");
        el.style.display = "block";

        if(e.file_name.endsWith(".wasm"))
        {
            const parts = e.file_name.split("/");
            el.textContent = "Fetching " + parts[parts.length - 1] + " ...";
            return;
        }

        if(e.file_index === e.file_count - 1 && e.loaded >= e.total - 2048)
        {
            // last file is (almost) loaded
            el.textContent = "Done downloading. Starting now ...";
            return;
        }

        var line = "Downloading images ";

        if(typeof e.file_index === "number" && e.file_count)
        {
            line += "[" + (e.file_index + 1) + "/" + e.file_count + "] ";
        }

        if(e.total && typeof e.loaded === "number")
        {
            var per100 = Math.floor(e.loaded / e.total * 100);
            per100 = Math.min(100, Math.max(0, per100));

            var per50 = Math.floor(per100 / 2);

            line += per100 + "% [";
            line += "#".repeat(per50);
            line += " ".repeat(50 - per50) + "]";
        }
        else
        {
            line += ".".repeat(progress_ticks++ % 50);
        }

        el.textContent = line;
    }

    function $(id)
    {
        return document.getElementById(id);
    }

    function onload()
    {
        if(!window.WebAssembly)
        {
            alert("Your browser is not supported because it doesn't support WebAssembly");
            return;
        }

        const script = document.createElement("script");
        script.src = "build/xterm.js";
        script.async = true;
        document.body.appendChild(script);

        var settings = {};

        $("start_emulation").onclick = function()
        {
            $("boot_options").style.display = "none";
            set_profile("custom");

            var images = [];
            var last_file;

            var floppy_file = $("floppy_image").files[0];
            if(floppy_file)
            {
                last_file = floppy_file;
                settings.fda = { buffer: floppy_file };
            }

            var cd_file = $("cd_image").files[0];
            if(cd_file)
            {
                last_file = cd_file;
                settings.cdrom = { buffer: cd_file };
            }

            var hda_file = $("hda_image").files[0];
            if(hda_file)
            {
                last_file = hda_file;
                settings.hda = { buffer: hda_file };
            }

            var hdb_file = $("hdb_image") && $("hdb_image").files[0];
            if(hdb_file)
            {
                last_file = hdb_file;
                settings.hdb = { buffer: hdb_file };
            }

            if($("multiboot_image"))
            {
                var multiboot_file = $("multiboot_image").files[0];
                if(multiboot_file)
                {
                    last_file = multiboot_file;
                    settings.multiboot = { buffer: multiboot_file };
                }
            }

            if(last_file)
            {
                set_title(last_file.name);
            }

            start_emulation(settings);
        };

        if(DEBUG)
        {
            debug_onload(settings);
        }

        const query_args = get_query_arguments();
        const host = query_args["cdn"] || (ON_LOCALHOST ? "images/" : "//k.copy.sh/");

        // Abandonware OS images are from https://winworldpc.com/library/operating-systems
        var oses = [
            {
                id: "archlinux",
                name: "Arch Linux",
                memory_size: 512 * 1024 * 1024,
                vga_memory_size: 8 * 1024 * 1024,
                state: {
                    url: host + "arch_state.bin.zst",
                },
                filesystem: {
                    baseurl: host + "arch/",
                },
            },
            {
                id: "archlinux-boot",
                name: "Arch Linux",
                memory_size: 512 * 1024 * 1024,
                vga_memory_size: 8 * 1024 * 1024,
                filesystem: {
                    baseurl: host + "arch/",
                    basefs: { url: host + "fs.json", },
                },
                cmdline: [
                    "rw apm=off vga=0x344 video=vesafb:ypan,vremap:8",
                    "root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose",
                    "mitigations=off audit=0",
                    "page_poison=on",
                    "tsc=reliable",
                    "random.trust_cpu=on",
                    "nowatchdog",
                    "init=/usr/bin/init-openrc net.ifnames=0 biosdevname=0",
                ].join(" "),
                bzimage_initrd_from_filesystem: true,
            },
            {
                id: "serenity",
                name: "SerenityOS",
                hda: {
                    url: host + "serenity-v2.img",
                    size: 700448768,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                memory_size: 512 * 1024 * 1024,
                state: { url: host + "serenity_state-v3.bin.zst", },
                homepage: "https://serenityos.org/",
                mac_address_translation: true,
            },
            {
                id: "serenity-boot",
                name: "SerenityOS",
                hda: {
                    url: host + "serenity-v2.img",
                    size: 700448768,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                memory_size: 512 * 1024 * 1024,
                homepage: "https://serenityos.org/",
            },
            {
                id: "serenity-old",
                name: "SerenityOS",
                hda: {
                    url: host + "serenity.img",
                    size: 876 * 1024 * 1024,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                memory_size: 512 * 1024 * 1024,
                state: { url: host + "serenity_state-v2.bin.zst", },
                homepage: "https://serenityos.org/",
            },
            {
                id: "serenity-old-boot",
                name: "SerenityOS",
                hda: {
                    url: host + "serenity.img",
                    size: 876 * 1024 * 1024,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                memory_size: 512 * 1024 * 1024,
                homepage: "https://serenityos.org/",
            },
            {
                id: "helenos",
                memory_size: 256 * 1024 * 1024,
                cdrom: {
                    url: host + "HelenOS-0.11.2-ia32.iso",
                    size: 25765888,
                    async: false,
                },
                name: "HelenOS",
                homepage: "http://www.helenos.org/",
            },
            {
                id: "haiku",
                memory_size: 512 * 1024 * 1024,
                hda: {
                    url: host + "haiku-v2.img",
                    size: 1 * 1024 * 1024 * 1024,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                state: {
                    url: host + "haiku_state-v2.bin.zst",
                },
                name: "Haiku",
                homepage: "https://www.haiku-os.org/",
            },
            {
                id: "haiku-boot",
                memory_size: 512 * 1024 * 1024,
                hda: {
                    url: host + "haiku-v2.img",
                    size: 1 * 1024 * 1024 * 1024,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "Haiku",
                homepage: "https://www.haiku-os.org/",
            },
            {
                id: "msdos",
                hda: {
                    url: host + "msdos.img",
                    size: 8 * 1024 * 1024,
                    async: false,
                },
                boot_order: 0x132,
                name: "MS-DOS",
            },
            {
                id: "freedos",
                fda: {
                    url: host + "freedos722.img",
                    size: 737280,
                    async: false,
                },
                name: "FreeDOS",
            },
            {
                id: "psychdos",
                hda: {
                    url: host + "psychdos.img",
                    size: 549453824,
                    async: true,
                    fixed_chunk_size: 256 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "PsychDOS",
                homepage: "https://psychoslinux.gitlab.io/DOS/INDEX.HTM",
            },
            {
                id: "oberon",
                hda: {
                    url: host + "oberon.img",
                    size: 24 * 1024 * 1024,
                    async: false,
                },
                name: "Oberon",
            },
            {
                id: "windows1",
                fda: {
                    url: host + "windows101.img",
                    size: 1474560,
                    async: false,
                },
                name: "Windows",
            },
            {
                id: "linux26",
                cdrom: {
                    url: host + "linux.iso",
                    size: 6547456,
                    async: false,
                },
                name: "Linux",
            },
            {
                id: "linux3",
                cdrom: {
                    url: host + "linux3.iso",
                    size: 8624128,
                    async: false,
                },
                name: "Linux",
            },
            {
                id: "linux4",
                cdrom: {
                    url: host + "linux4.iso",
                    size: 7731200,
                    async: false,
                },
                name: "Linux",
                filesystem: {},
            },
            {
                id: "buildroot",
                bzimage: {
                    url: host + "buildroot-bzimage.bin",
                    size: 5166352,
                    async: false,
                },
                name: "Buildroot Linux",
                filesystem: {},
                cmdline: "tsc=reliable mitigations=off random.trust_cpu=on",
            },
            {
                id: "nodeos",
                bzimage: {
                    url: host + "nodeos-kernel.bin",
                    size: 14452000,
                    async: false,
                },
                name: "NodeOS",
                cmdline: "tsc=reliable mitigations=off random.trust_cpu=on",
            },
            {
                id: "dsl",
                memory_size: 256 * 1024 * 1024,
                cdrom: {
                    url: host + "dsl-4.11.rc2.iso",
                    size: 52824064,
                    async: false,
                },
                name: "Damn Small Linux",
                homepage: "http://www.damnsmalllinux.org/",
            },
            {
                id: "minix",
                name: "Minix",
                memory_size: 256 * 1024 * 1024,
                cdrom: {
                    url: host + "minix-3.3.0.iso",
                    size: 605581312,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                homepage: "https://www.minix3.org/",
            },
            {
                id: "kolibrios",
                fda: {
                    url: ON_LOCALHOST ?
                            host + "kolibri.img" :
                            "//builds.kolibrios.org/eng/data/data/kolibri.img",
                    size: 1474560,
                    async: false,
                },
                name: "KolibriOS",
                homepage: "https://kolibrios.org/en/",
            },
            {
                id: "kolibrios-fallback",
                fda: {
                    url: host + "kolibri.img",
                    size: 1474560,
                    async: false,
                },
                name: "KolibriOS",
            },
            {
                id: "openbsd",
                hda: {
                    url: host + "openbsd.img",
                    size: 1073741824,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                state: {
                    url: host + "openbsd_state.bin.zst",
                },
                memory_size: 256 * 1024 * 1024,
                name: "OpenBSD",
            },
            {
                id: "openbsd-boot",
                hda: {
                    url: host + "openbsd.img",
                    size: 1073741824,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                memory_size: 256 * 1024 * 1024,
                name: "OpenBSD",
                //acpi: true, // doesn't seem to work
            },
            {
                id: "netbsd",
                hda: {
                    url: host + "netbsd.img",
                    size: 511000064,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                memory_size: 256 * 1024 * 1024,
                name: "NetBSD",
            },
            {
                id: "solos",
                fda: {
                    url: host + "os8.img",
                    async: false,
                    size: 1474560,
                },
                name: "Sol OS",
                homepage: "http://oby.ro/os/",
            },
            {
                id: "bootchess",
                fda: {
                    url: host + "bootchess.img",
                    async: false,
                    size: 1474560,
                },
                name: "BootChess",
                homepage: "http://www.pouet.net/prod.php?which=64962",
            },
            {
                id: "bootbasic",
                fda: {
                    url: host + "bootbasic.img",
                    async: false,
                    size: 1474560,
                },
                name: "bootBASIC",
                homepage: "https://github.com/nanochess/bootBASIC",
            },
            {
                id: "sectorlisp",
                fda: {
                    url: host + "sectorlisp-friendly.bin",
                    async: false,
                    size: 512,
                },
                name: "SectorLISP",
                homepage: "https://justine.lol/sectorlisp2/",
            },
            {
                id: "sectorforth",
                fda: {
                    url: host + "sectorforth.img",
                    async: false,
                    size: 512,
                },
                name: "sectorforth",
                homepage: "https://github.com/cesarblum/sectorforth",
            },
            {
                id: "floppybird",
                fda: {
                    url: host + "floppybird.img",
                    async: false,
                    size: 1474560,
                },
                name: "Floppy Bird",
                homepage: "http://mihail.co/floppybird",
            },
            {
                id: "windows2000",
                memory_size: 512 * 1024 * 1024,
                hda: {
                    url: host + "windows2k.img",
                    size: 2 * 1024 * 1024 * 1024,
                    async: true,
                    fixed_chunk_size: 256 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "Windows 2000",
                state: {
                    url: host + "windows2k_state-v2.bin.zst",
                },
                mac_address_translation: true,
            },
            {
                id: "windows2000-boot",
                memory_size: 512 * 1024 * 1024,
                hda: {
                    url: host + "windows2k.img",
                    size: 2 * 1024 * 1024 * 1024,
                    async: true,
                    fixed_chunk_size: 256 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                boot_order: 0x132,
                name: "Windows 2000",
            },
            {
                id: "windowsnt4",
                memory_size: 512 * 1024 * 1024,
                hda: {
                    url: host + "winnt4_noacpi.img",
                    size: 523837440,
                    async: true,
                    fixed_chunk_size: 256 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "Windows NT 4.0",
                cpuid_level: 2,
            },
            {
                id: "windowsnt3",
                memory_size: 256 * 1024 * 1024,
                hda: {
                    url: host + "winnt31.img",
                    size: 87 * 1024 * 1024,
                    async: true,
                    fixed_chunk_size: 256 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "Windows NT 3.1",
            },
            {
                id: "windows98",
                memory_size: 128 * 1024 * 1024,
                hda: {
                    url: host + "windows98.img",
                    size: 300 * 1024 * 1024,
                    async: true,
                    fixed_chunk_size: 256 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "Windows 98",
                state: {
                    url: host + "windows98_state.bin.zst",
                },
                mac_address_translation: true,
            },
            {
                id: "windows98-boot",
                memory_size: 128 * 1024 * 1024,
                hda: {
                    url: host + "windows98.img",
                    size: 300 * 1024 * 1024,
                    async: true,
                    fixed_chunk_size: 256 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "Windows 98",
            },
            {
                id: "windows95",
                memory_size: 32 * 1024 * 1024,
                hda: {
                    url: host + "w95.img",
                    size: 242049024,
                    async: true,
                    fixed_chunk_size: 256 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "Windows 95",
                state: {
                    url: host + "windows95_state.bin.zst",
                },
            },
            {
                id: "windows95-boot",
                memory_size: 32 * 1024 * 1024,
                hda: {
                    url: host + "w95.img",
                    size: 242049024,
                    async: true,
                    fixed_chunk_size: 256 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "Windows 95",
            },
            {
                id: "windows30",
                memory_size: 64 * 1024 * 1024,
                cdrom: {
                    url: host + "Win30.iso",
                    async: false,
                },
                name: "Windows 3.0",
            },
            {
                id: "windows31",
                memory_size: 64 * 1024 * 1024,
                hda: {
                    url: host + "win31.img",
                    async: false,
                    size: 34463744,
                },
                name: "Windows 3.1",
            },
            {
                id: "freebsd",
                memory_size: 256 * 1024 * 1024,
                hda: {
                    url: host + "freebsd.img",
                    size: 2147483648,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                state: {
                    url: host + "freebsd_state.bin.zst",
                },
                name: "FreeBSD",
            },
            {
                id: "freebsd-boot",
                memory_size: 256 * 1024 * 1024,
                hda: {
                    url: host + "freebsd.img",
                    size: 2147483648,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "FreeBSD",
            },
            {
                id: "reactos-livecd",
                memory_size: 256 * 1024 * 1024,
                hda: {
                    url: host + "reactos-livecd-0.4.15-dev-73-g03c09c9-x86-gcc-lin-dbg.iso",
                    size: 250609664,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "ReactOS",
                homepage: "https://reactos.org/",
            },
            {
                id: "reactos",
                memory_size: 512 * 1024 * 1024,
                hda: {
                    url: host + "reactos.img",
                    size: 500 * 1024 * 1024,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                state: {
                    url: host + "reactos_state.bin.zst",
                },
                mac_address_translation: true,
                name: "ReactOS",
                homepage: "https://reactos.org/",
            },
            {
                id: "reactos-boot",
                memory_size: 512 * 1024 * 1024,
                hda: {
                    url: host + "reactos.img",
                    size: 500 * 1024 * 1024,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "ReactOS",
                homepage: "https://reactos.org/",
            },
            {
                id: "skift",
                cdrom: {
                    url: host + "skift-20200910.iso",
                    size: 64452608,
                    async: false,
                },
                name: "Skift",
                homepage: "https://skiftos.org/",
            },
            {
                id: "snowdrop",
                fda: {
                    url: host + "snowdrop.img",
                    size: 1440 * 1024,
                    async: false,
                },
                name: "Snowdrop",
                homepage: "http://www.sebastianmihai.com/snowdrop/",
            },
            {
                id: "openwrt",
                hda: {
                    url: host + "openwrt-18.06.1-x86-legacy-combined-squashfs.img",
                    size: 19846474,
                    async: false,
                },
                name: "OpenWrt",
            },
            {
                id: "qnx",
                fda: {
                    url: host + "qnx-demo-network-4.05.img",
                    size: 1474560,
                    async: false
                },
                name: "QNX 4.05",
            },
            {
                id: "9front",
                memory_size: 128 * 1024 * 1024,
                hda: {
                    url: host + "9front-8963.f84cf1e60427675514fb056cc1723e45da01e043.386.iso",
                    size: 477452288,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                state: {
                    url: host + "9front_state-v2.bin.zst",
                },
                acpi: true,
                name: "9front",
                homepage: "https://9front.org/",
            },
            {
                id: "9front-boot",
                memory_size: 128 * 1024 * 1024,
                hda: {
                    url: host + "9front-8963.f84cf1e60427675514fb056cc1723e45da01e043.386.iso",
                    size: 477452288,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                acpi: true,
                name: "9front",
                homepage: "https://9front.org/",
            },
            {
                id: "mobius",
                fda: {
                    url: host + "mobius-fd-release5.img",
                    size: 1474560,
                    async: false,
                },
                name: "Mobius",
            },
            {
                id: "android",
                memory_size: 512 * 1024 * 1024,
                cdrom: {
                    url: host + "android-x86-1.6-r2.iso",
                    size: 54661120,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "Android",
            },
            {
                id: "android4",
                memory_size: 512 * 1024 * 1024,
                cdrom: {
                    url: host + "android_x86_nonsse3_4.4r1_20140904.iso",
                    size: 247463936,
                    async: true,
                    fixed_chunk_size: 1024 * 1024,
                    use_parts: !ON_LOCALHOST,
                },
                name: "Android",
            },
            {
                id: "tinycore",
                memory_size: 256 * 1024 * 1024,
                hda: {
                    url: host + "TinyCore-11.0.iso",
                    async: false,
                },
                name: "Tinycore",
                homepage: "http://www.tinycorelinux.net/",
            },
            {
                id: "freenos",
                memory_size: 256 * 1024 * 1024,
                cdrom: {
                    url: host + "FreeNOS-1.0.3.iso",
                    async: false,
                    size: 11014144,
                },
                name: "FreeNOS",
                acpi: true,
                homepage: "http://www.freenos.org/",
            },
        ];

        if(DEBUG)
        {
            // see tests/kvm-unit-tests/x86/
            var tests = [
                "realmode",
                // All tests below require an APIC
                "cmpxchg8b",
                "port80",
                "setjmp",
                "sieve",
                "hypercall", // crashes
                "init", // stops execution
                "msr", // TODO: Expects 64 bit msrs
                "smap", // test stops, SMAP not enabled
                "tsc_adjust", // TODO: IA32_TSC_ADJUST
                "tsc", // TODO: rdtscp
                "rmap_chain", // crashes
                "memory", // missing mfence (uninteresting)
                "taskswitch", // TODO: Jump
                "taskswitch2", // TODO: Call TSS
                "eventinj", // Missing #nt
                "ioapic",
                "apic",
            ];

            for(let test of tests)
            {
                oses.push({
                    name: "Test case: " + test,
                    id: "test-" + test,
                    memory_size: 128 * 1024 * 1024,
                    multiboot: { url: "tests/kvm-unit-tests/x86/" + test + ".flat", }
                });
            }
        }

        var profile = query_args["profile"];

        if(!profile && !DEBUG)
        {
            const link = document.createElement("link");
            link.rel = "prefetch";
            link.href = "build/v86.wasm";
            document.head.appendChild(link);
        }

        if(query_args["use_bochs_bios"])
        {
            settings.use_bochs_bios = true;
        }

        const m = parseInt(query_args["m"], 10);
        if(m > 0)
        {
            settings.memory_size = Math.max(16, m) * 1024 * 1024;
        }

        const vram = parseInt(query_args["vram"], 10);
        if(vram > 0)
        {
            settings.vga_memory_size = vram * 1024 * 1024;
        }

        settings.networking_proxy = query_args["networking_proxy"];
        settings.audio = query_args["audio"] !== "0";
        settings.acpi = query_args["acpi"];

        for(var i = 0; i < oses.length; i++)
        {
            var infos = oses[i];

            if(profile === infos.id)
            {
                start_profile(infos);
                return;
            }

            var element = $("start_" + infos.id);

            if(element)
            {
                element.onclick = function(infos, element, e)
                {
                    e.preventDefault();
                    set_profile(infos.id);
                    element.blur();

                    start_profile(infos);
                }.bind(this, infos, element);
            }
        }

        if(profile === "custom")
        {
            if(query_args["hda.url"])
            {
                settings.hda = {
                    size: parseInt(query_args["hda.size"], 10) || undefined,
                    url: query_args["hda.url"],
                    async: true,
                };
            }

            if(query_args["cdrom.url"])
            {
                settings.cdrom = {
                    size: parseInt(query_args["cdrom.size"], 10) || undefined,
                    url: query_args["cdrom.url"],
                    async: true,
                };
            }

            if(query_args["fda.url"])
            {
                settings.fda = {
                    size: parseInt(query_args["fda.size"], 10) || undefined,
                    url: query_args["fda.url"],
                    async: false,
                };
            }

            if(settings.fda || settings.cdrom || settings.hda)
            {
                $("boot_options").style.display = "none";

                start_emulation(settings, done);
            }
        }
        else if(/^[a-zA-Z0-9\-_]+\/[a-zA-Z0-9\-_]+$/g.test(profile))
        {
            // experimental: server that allows user-uploaded images

            const base = "https://v86-user-images.b-cdn.net/" + profile;

            fetch(base + "/profile.json")
                .then(response => response.json())
                .then(p => {
                    function handle_image(o)
                    {
                        return o && { url: base + "/" + o["url"], async: o["async"], size: o["size"] };
                    }

                    start_profile({
                        id: p["id"],
                        name: p["name"],
                        memory_size: p["memory_size"],
                        vga_memory_size: p["vga_memory_size"],
                        acpi: p["acpi"],
                        boot_order: p["boot_order"],
                        hda: handle_image(p["hda"]),
                        cdrom: handle_image(p["cdrom"]),
                        fda: handle_image(p["fda"]),
                        multiboot: handle_image(p["multiboot"]),
                        bzimage: handle_image(p["bzimage"]),
                        initrd: handle_image(p["initrd"]),
                    });
                })
                .catch(e => alert("Profile not found: " + profile));
        }

        function start_profile(infos)
        {
            $("boot_options").style.display = "none";
            set_title(infos.name);

            settings.filesystem = infos.filesystem;

            if(infos.state)
            {
                $("reset").style.display = "none";
                settings.initial_state = infos.state;
            }

            settings.fda = infos.fda;
            settings.cdrom = infos.cdrom;
            settings.hda = infos.hda;
            settings.multiboot = infos.multiboot;
            settings.bzimage = infos.bzimage;
            settings.initrd = infos.initrd;
            settings.cmdline = infos.cmdline;
            settings.bzimage_initrd_from_filesystem = infos.bzimage_initrd_from_filesystem;
            settings.mac_address_translation = infos.mac_address_translation;
            settings.cpuid_level = infos.cpuid_level;

            settings.acpi = (!infos.state && settings.acpi !== undefined) ? settings.acpi : infos.acpi;
            settings.memory_size = (!infos.state && settings.memory_size) ? settings.memory_size : infos.memory_size;
            settings.vga_memory_size = (!infos.state && settings.vga_memory_size) ? settings.vga_memory_size : infos.vga_memory_size;

            settings.id = infos.id;

            if(infos.boot_order !== undefined)
            {
                settings.boot_order = infos.boot_order;
            }

            let chunk_size = parseInt(query_args["chunk_size"], 10);
            if(chunk_size >= 0)
            {
                if(chunk_size)
                {
                    chunk_size = Math.min(4 * 1024 * 1024, Math.max(512, chunk_size));
                    chunk_size = 1 << Math.ceil(Math.log2(chunk_size));
                }
                else
                {
                    chunk_size = undefined;
                }

                if(settings.hda)
                {
                    settings.hda.fixed_chunk_size = chunk_size;
                }

                if(settings.cdrom)
                {
                    settings.cdrom.fixed_chunk_size = chunk_size;
                }
            }

            if(!DEBUG && infos.homepage)
            {
                $("description").style.display = "block";
                const link = document.createElement("a");
                link.href = infos.homepage;
                link.textContent = infos.name;
                link.target = "_blank";
                $("description").appendChild(document.createTextNode("Running "));
                $("description").appendChild(link);
            }

            start_emulation(settings, done);
        }

        function done(emulator)
        {
            if(query_args["c"])
            {
                setTimeout(function()
                {
                    //emulator.serial0_send(query_args["c"] + "\n");
                    emulator.keyboard_send_text(query_args["c"] + "\n");
                }, 25);
            }
        }
    }

    function debug_onload(settings)
    {
        // called on window.onload, in debug mode

        var log_levels = $("log_levels");

        if(log_levels)
        {
            for(var i = 0; i < LOG_NAMES.length; i++)
            {
                var mask = LOG_NAMES[i][0];

                if(mask === 1)
                    continue;

                var name = LOG_NAMES[i][1].toLowerCase(),
                    input = document.createElement("input"),
                    label = document.createElement("label");

                input.type = "checkbox";

                label.htmlFor = input.id = "log_" + name;

                if(LOG_LEVEL & mask)
                {
                    input.checked = true;
                }
                input.mask = mask;

                label.appendChild(input);
                label.appendChild(document.createTextNode(v86util.pads(name, 4) + " "));
                log_levels.appendChild(label);

                if(i === Math.floor(LOG_NAMES.length / 2))
                {
                    log_levels.appendChild(document.createTextNode("\n"));
                }
            }

            log_levels.onchange = function(e)
            {
                var target = e.target,
                    mask = target.mask;

                if(target.checked)
                {
                    LOG_LEVEL |= mask;
                }
                else
                {
                    LOG_LEVEL &= ~mask;
                }

                target.blur();
            };
        }
    }

    window.addEventListener("load", onload, false);

    // old webkit fires popstate on every load, fuck webkit
    // https://code.google.com/p/chromium/issues/detail?id=63040
    window.addEventListener("load", function()
    {
        setTimeout(function()
        {
            window.addEventListener("popstate", onpopstate);
        }, 0);
    });

    // works in firefox and chromium
    if(document.readyState === "complete")
    {
        onload();
    }

    /** @param {?=} done */
    function start_emulation(settings, done)
    {
        /** @const */
        var MB = 1024 * 1024;

        var memory_size = settings.memory_size;

        if(!memory_size)
        {
            memory_size = parseInt($("memory_size").value, 10) * MB;

            if(!memory_size)
            {
                alert("Invalid memory size - reset to 128MB");
                memory_size = 128 * MB;
            }
        }

        var vga_memory_size = settings.vga_memory_size;

        if(!vga_memory_size)
        {
            vga_memory_size = parseInt($("video_memory_size").value, 10) * MB;

            if(!vga_memory_size)
            {
                alert("Invalid video memory size - reset to 8MB");
                vga_memory_size = 8 * MB;
            }
        }

        if(!settings.fda)
        {
            var floppy_file = $("floppy_image").files[0];
            if(floppy_file)
            {
                settings.fda = { buffer: floppy_file };
            }
        }

        if(!settings.bzimage)
        {
            var bzimage = $("bzimage").files[0];
            if(bzimage)
            {
                settings.bzimage = { buffer: bzimage };
            }
        }

        if(!settings.initrd)
        {
            var initrd = $("initrd").files[0];
            if(initrd)
            {
                settings.initrd = { buffer: initrd };
            }
        }

        const networking_proxy = settings.networking_proxy === undefined ? $("networking_proxy").value : settings.networking_proxy;
        const disable_audio = settings.audio === undefined ? $("disable_audio").checked : !settings.audio;
        const enable_acpi = settings.acpi === undefined ? $("enable_acpi").checked : settings.acpi;

        /** @const */
        var BIOSPATH = "bios/";

        if(settings.use_bochs_bios)
        {
            var biosfile = "bochs-bios.bin";
            var vgabiosfile = "bochs-vgabios.bin";
        }
        else
        {
            var biosfile = DEBUG ? "seabios-debug.bin" : "seabios.bin";
            var vgabiosfile = DEBUG ? "vgabios-debug.bin" : "vgabios.bin";
        }

        var bios;
        var vga_bios;

        // a bios is only needed if the machine is booted
        if(!settings.initial_state)
        {
            bios = {
                url: BIOSPATH + biosfile,
            };
            vga_bios = {
                url: BIOSPATH + vgabiosfile,
            };
        }

        var emulator = new V86Starter({
            "memory_size": memory_size,
            "vga_memory_size": vga_memory_size,

            "screen_container": $("screen_container"),
            "serial_container_xtermjs": $("terminal"),

            "boot_order": settings.boot_order || parseInt($("boot_order").value, 16) || 0,

            "network_relay_url": ON_LOCALHOST ? "ws://localhost:8080/" : networking_proxy,

            "bios": bios,
            "vga_bios": vga_bios,

            "fda": settings.fda,
            "hda": settings.hda,
            "hdb": settings.hdb,
            "cdrom": settings.cdrom,

            "multiboot": settings.multiboot,
            "bzimage": settings.bzimage,
            "initrd": settings.initrd,
            "cmdline": settings.cmdline,
            "bzimage_initrd_from_filesystem": settings.bzimage_initrd_from_filesystem,

            "acpi": enable_acpi,
            "initial_state": settings.initial_state,
            "filesystem": settings.filesystem || {},
            "disable_speaker": disable_audio,
            "mac_address_translation": settings.mac_address_translation,
            "cpuid_level": settings.cpuid_level,

            "autostart": true,
        });

        if(DEBUG) window["emulator"] = emulator;

        emulator.add_listener("emulator-ready", function()
        {
            if(DEBUG)
            {
                debug_start(emulator);
            }

            if(emulator.v86.cpu.wm.exports["profiler_is_enabled"]())
            {
                const CLEAR_STATS = false;

                var panel = document.createElement("pre");
                document.body.appendChild(panel);

                setInterval(function()
                    {
                        if(!emulator.is_running())
                        {
                            return;
                        }

                        const text = print_stats.stats_to_string(emulator.v86.cpu);
                        panel.textContent = text;

                        CLEAR_STATS && emulator.v86.cpu.clear_opstats();
                    }, CLEAR_STATS ? 5000 : 1000);
            }

            if(settings.id === "dsl" || settings.id === "helenos")
            {
                setTimeout(() => {
                    // hack: Start automatically
                    emulator.keyboard_send_text("\n");
                }, 3000);
            }
            else if(settings.id === "android" || settings.id === "android4")
            {
                setTimeout(() => {
                    // hack: select vesa mode and start automatically
                    emulator.keyboard_send_scancodes([0xe050, 0xe050 | 0x80]);
                    emulator.keyboard_send_text("\n");
                }, 3000);
            }

            init_ui(settings, emulator);

            done && done(emulator);
        });

        emulator.add_listener("download-progress", function(e)
        {
            show_progress(e);
        });

        emulator.add_listener("download-error", function(e)
        {
            var el = $("loading");
            el.style.display = "block";
            el.textContent = "Loading " + e.file_name + " failed. Check your connection " +
                             "and reload the page to try again.";
        });
    }

    /**
     * @param {Object} settings
     * @param {V86Starter} emulator
     */
    function init_ui(settings, emulator)
    {
        $("boot_options").style.display = "none";
        $("loading").style.display = "none";
        $("runtime_options").style.display = "block";
        $("runtime_infos").style.display = "block";
        $("screen_container").style.display = "block";

        if(settings.filesystem)
        {
            init_filesystem_panel(emulator);
        }
        else
        {
            emulator.add_listener("9p-attach", function()
            {
                init_filesystem_panel(emulator);
            });
        }

        $("run").onclick = function()
        {
            if(emulator.is_running())
            {
                $("run").value = "Run";
                emulator.stop();
            }
            else
            {
                $("run").value = "Pause";
                emulator.run();
            }

            $("run").blur();
        };

        $("exit").onclick = function()
        {
            emulator.stop();
            location.href = location.pathname;
        };

        $("lock_mouse").onclick = function()
        {
            if(!mouse_is_enabled)
            {
                $("toggle_mouse").onclick();
            }

            emulator.lock_mouse();
            $("lock_mouse").blur();
        };

        var mouse_is_enabled = true;

        $("toggle_mouse").onclick = function()
        {
            mouse_is_enabled = !mouse_is_enabled;

            emulator.mouse_set_status(mouse_is_enabled);
            $("toggle_mouse").value = (mouse_is_enabled ? "Dis" : "En") + "able mouse";
            $("toggle_mouse").blur();
        };


        var last_tick = 0;
        var running_time = 0;
        var last_instr_counter = 0;
        var interval = null;
        var os_uses_mouse = false;
        var total_instructions = 0;

        function update_info()
        {
            var now = Date.now();

            var instruction_counter = emulator.get_instruction_counter();

            if(instruction_counter < last_instr_counter)
            {
                // 32-bit wrap-around
                last_instr_counter -= 0x100000000;
            }

            var last_ips = instruction_counter - last_instr_counter;
            last_instr_counter = instruction_counter;
            total_instructions += last_ips;

            var delta_time = now - last_tick;

            if(delta_time)
            {
                running_time += delta_time;
                last_tick = now;

                $("speed").textContent = (last_ips / 1000 / delta_time).toFixed(1);
                $("avg_speed").textContent = (total_instructions / 1000 / running_time).toFixed(1);
                $("running_time").textContent = format_timestamp(running_time / 1000 | 0);
            }
        }

        emulator.add_listener("emulator-started", function()
        {
            last_tick = Date.now();
            interval = setInterval(update_info, 1000);
        });

        emulator.add_listener("emulator-stopped", function()
        {
            update_info();
            if(interval !== null)
            {
                clearInterval(interval);
            }
        });

        var stats_9p = {
            read: 0,
            write: 0,
            files: [],
        };

        emulator.add_listener("9p-read-start", function(args)
        {
            const file = args[0];
            stats_9p.files.push(file);
            $("info_filesystem").style.display = "block";
            $("info_filesystem_status").textContent = "Loading ...";
            $("info_filesystem_last_file").textContent = file;
        });
        emulator.add_listener("9p-read-end", function(args)
        {
            stats_9p.read += args[1];
            $("info_filesystem_bytes_read").textContent = stats_9p.read;

            const file = args[0];
            stats_9p.files = stats_9p.files.filter(f => f !== file);

            if(stats_9p.files[0])
            {
                $("info_filesystem_last_file").textContent = stats_9p.files[0];
            }
            else
            {
                $("info_filesystem_status").textContent = "Idle";
            }
        });
        emulator.add_listener("9p-write-end", function(args)
        {
            stats_9p.write += args[1];
            $("info_filesystem_bytes_written").textContent = stats_9p.write;

            if(!stats_9p.files[0])
            {
                $("info_filesystem_last_file").textContent = args[0];
            }
        });

        var stats_storage = {
            read: 0,
            read_sectors: 0,
            write: 0,
            write_sectors: 0,
        };

        emulator.add_listener("ide-read-start", function()
        {
            $("info_storage").style.display = "block";
            $("info_storage_status").textContent = "Loading ...";
        });
        emulator.add_listener("ide-read-end", function(args)
        {
            stats_storage.read += args[1];
            stats_storage.read_sectors += args[2];

            $("info_storage_status").textContent = "Idle";
            $("info_storage_bytes_read").textContent = stats_storage.read;
            $("info_storage_sectors_read").textContent = stats_storage.read_sectors;
        });
        emulator.add_listener("ide-write-end", function(args)
        {
            stats_storage.write += args[1];
            stats_storage.write_sectors += args[2];

            $("info_storage_bytes_written").textContent = stats_storage.write;
            $("info_storage_sectors_written").textContent = stats_storage.write_sectors;
        });

        var stats_net = {
            bytes_transmitted: 0,
            bytes_received: 0,
        };

        emulator.add_listener("eth-receive-end", function(args)
        {
            stats_net.bytes_received += args[0];

            $("info_network").style.display = "block";
            $("info_network_bytes_received").textContent = stats_net.bytes_received;
        });
        emulator.add_listener("eth-transmit-end", function(args)
        {
            stats_net.bytes_transmitted += args[0];

            $("info_network").style.display = "block";
            $("info_network_bytes_transmitted").textContent = stats_net.bytes_transmitted;
        });


        emulator.add_listener("mouse-enable", function(is_enabled)
        {
            os_uses_mouse = is_enabled;
            $("info_mouse_enabled").textContent = is_enabled ? "Yes" : "No";
        });

        emulator.add_listener("screen-set-mode", function(is_graphical)
        {
            if(is_graphical)
            {
                $("info_vga_mode").textContent = "Graphical";
            }
            else
            {
                $("info_vga_mode").textContent = "Text";
                $("info_res").textContent = "-";
                $("info_bpp").textContent = "-";
            }
        });
        emulator.add_listener("screen-set-size-graphical", function(args)
        {
            $("info_res").textContent = args[0] + "x" + args[1];
            $("info_bpp").textContent = args[4];
        });


        $("reset").onclick = function()
        {
            emulator.restart();
            $("reset").blur();
        };

        add_image_download_button(settings.hda, "hda");
        add_image_download_button(settings.hdb, "hdb");
        add_image_download_button(settings.fda, "fda");
        add_image_download_button(settings.fdb, "fdb");
        add_image_download_button(settings.cdrom, "cdrom");

        function add_image_download_button(obj, type)
        {
            var elem = $("get_" + type + "_image");

            if(!obj || obj.size > 100 * 1024 * 1024)
            {
                elem.style.display = "none";
                return;
            }

            elem.onclick = function(e)
            {
                let buffer = emulator.disk_images[type];
                let filename = buffer.file && buffer.file.name || (settings.id + (type === "cdrom" ? ".iso" : ".img"));

                if(buffer.get_as_file)
                {
                    var file = buffer.get_as_file(filename);
                    download(file, filename);
                }
                else
                {
                    buffer.get_buffer(function(b)
                    {
                        if(b)
                        {
                            dump_file(b, filename);
                        }
                        else
                        {
                            alert("The file could not be loaded. Maybe it's too big?");
                        }
                    });
                }

                elem.blur();
            };
        }

        $("memory_dump").onclick = function()
        {
            const mem8 = emulator.v86.cpu.mem8;
            dump_file(new Uint8Array(mem8.buffer, mem8.byteOffset, mem8.length), "v86memory.bin");
            $("memory_dump").blur();
        };

        //$("memory_dump_dmp").onclick = function()
        //{
        //    var memory = emulator.v86.cpu.mem8;
        //    var memory_size = memory.length;
        //    var page_size = 4096;
        //    var header = new Uint8Array(4096);
        //    var header32 = new Int32Array(header.buffer);

        //    header32[0] = 0x45474150; // 'PAGE'
        //    header32[1] = 0x504D5544; // 'DUMP'

        //    header32[0x10 >> 2] = emulator.v86.cpu.cr[3]; // DirectoryTableBase
        //    header32[0x24 >> 2] = 1; // NumberProcessors
        //    header32[0xf88 >> 2] = 1; // DumpType: full dump
        //    header32[0xfa0 >> 2] = header.length + memory_size; // RequiredDumpSpace

        //    header32[0x064 + 0 >> 2] = 1; // NumberOfRuns
        //    header32[0x064 + 4 >> 2] = memory_size / page_size; // NumberOfPages
        //    header32[0x064 + 8 >> 2] = 0; // BasePage
        //    header32[0x064 + 12 >> 2] = memory_size / page_size; // PageCount

        //    dump_file([header, memory], "v86memory.dmp");

        //    $("memory_dump_dmp").blur();
        //};

        $("capture_network_traffic").onclick = function()
        {
            this.value = "0 packets";

            let capture = [];

            function do_capture(direction, data)
            {
                capture.push({ direction, time: performance.now() / 1000, hex_dump: hex_dump(data) });
                $("capture_network_traffic").value = capture.length + " packets";
            }

            emulator.emulator_bus.register("net0-receive", do_capture.bind(this, "I"));
            emulator.add_listener("net0-send", do_capture.bind(this, "O"));

            this.onclick = function()
            {
                const capture_raw = capture.map(({ direction, time, hex_dump }) => {
                    // https://www.wireshark.org/docs/wsug_html_chunked/ChIOImportSection.html
                    // In wireshark: file -> import from hex -> tick direction indication, timestamp %s.%f
                    return direction + " " + time.toFixed(6) + hex_dump + "\n";
                }).join("");
                dump_file(capture_raw, "traffic.hex");
                capture = [];
                this.value = "0 packets";
            };
        };


        $("save_state").onclick = async function()
        {
            const result = await emulator.save_state();
            dump_file(result, "v86state.bin");

            $("save_state").blur();
        };

        $("load_state").onclick = function()
        {
            $("load_state_input").click();
            $("load_state").blur();
        };

        $("load_state_input").onchange = async function()
        {
            var file = this.files[0];

            if(!file)
            {
                return;
            }

            var was_running = emulator.is_running();

            if(was_running)
            {
                await emulator.stop();
            }

            var filereader = new FileReader();
            filereader.onload = async function(e)
            {
                try
                {
                    await emulator.restore_state(e.target.result);
                }
                catch(err)
                {
                    alert("Something bad happened while restoring the state:\n" + err + "\n\n" +
                          "Note that the current configuration must be the same as the original");
                    throw err;
                }

                if(was_running)
                {
                    emulator.run();
                }
            };
            filereader.readAsArrayBuffer(file);

            this.value = "";
        };

        $("ctrlaltdel").onclick = function()
        {
            emulator.keyboard_send_scancodes([
                0x1D, // ctrl
                0x38, // alt
                0x53, // delete

                // break codes
                0x1D | 0x80,
                0x38 | 0x80,
                0x53 | 0x80,
            ]);

            $("ctrlaltdel").blur();
        };

        $("alttab").onclick = function()
        {
            emulator.keyboard_send_scancodes([
                0x38, // alt
                0x0F, // tab
            ]);

            setTimeout(function()
            {
                emulator.keyboard_send_scancodes([
                    0x38 | 0x80,
                    0x0F | 0x80,
                ]);
            }, 100);

            $("alttab").blur();
        };

        $("scale").onchange = function()
        {
            var n = parseFloat(this.value);

            if(n || n > 0)
            {
                emulator.screen_set_scale(n, n);
            }
        };

        $("fullscreen").onclick = function()
        {
            emulator.screen_go_fullscreen();
        };

        $("screen_container").onclick = function()
        {
            if(mouse_is_enabled && os_uses_mouse)
            {
                emulator.lock_mouse();
                $("lock_mouse").blur();
            }
            else
            {
                // allow text selection
                if(window.getSelection().isCollapsed)
                {
                    let phone_keyboard = document.getElementsByClassName("phone_keyboard")[0];

                    // stop mobile browser from scrolling into view when the keyboard is shown
                    phone_keyboard.style.top = document.body.scrollTop + 100 + "px";
                    phone_keyboard.style.left = document.body.scrollLeft + 100 + "px";

                    phone_keyboard.focus();
                }
            }
        };

        const phone_keyboard = document.getElementsByClassName("phone_keyboard")[0];

        phone_keyboard.setAttribute("autocorrect", "off");
        phone_keyboard.setAttribute("autocapitalize", "off");
        phone_keyboard.setAttribute("spellcheck", "false");
        phone_keyboard.tabIndex = 0;

        $("screen_container").addEventListener("mousedown", e =>
        {
            phone_keyboard.focus();
        }, false);

        $("take_screenshot").onclick = function()
        {
            emulator.screen_make_screenshot();
            $("take_screenshot").blur();
        };

        if(emulator.speaker_adapter)
        {
            let is_muted = false;

            $("mute").onclick = function()
            {
                if(is_muted)
                {
                    emulator.speaker_adapter.mixer.set_volume(1, undefined);
                    is_muted = false;
                    $("mute").value = "Mute";
                }
                else
                {
                    emulator.speaker_adapter.mixer.set_volume(0, undefined);
                    is_muted = true;
                    $("mute").value = "Unmute";
                }

                $("mute").blur();
            };
        }
        else
        {
            $("mute").remove();
        }

        window.addEventListener("keydown", ctrl_w_rescue, false);
        window.addEventListener("keyup", ctrl_w_rescue, false);
        window.addEventListener("blur", ctrl_w_rescue, false);

        function ctrl_w_rescue(e)
        {
            if(e.ctrlKey)
            {
                window.onbeforeunload = function()
                {
                    window.onbeforeunload = null;
                    return "CTRL-W cannot be sent to the emulator.";
                };
            }
            else
            {
                window.onbeforeunload = null;
            }
        }
    }

    function init_filesystem_panel(emulator)
    {
        $("filesystem_panel").style.display = "block";

        $("filesystem_send_file").onchange = function()
        {
            Array.prototype.forEach.call(this.files, function(file)
            {
                var loader = new v86util.SyncFileBuffer(file);
                loader.onload = function()
                {
                    loader.get_buffer(async function(buffer)
                    {
                        await emulator.create_file("/" + file.name, new Uint8Array(buffer));
                    });
                };
                loader.load();
            }, this);

            this.value = "";
            this.blur();
        };

        $("filesystem_get_file").onkeypress = async function(e)
        {
            if(e.which !== 13)
            {
                return;
            }

            this.disabled = true;

            let result;
            try
            {
                 result = await emulator.read_file(this.value);
            }
            catch(err)
            {
                console.log(err);
            }

            this.disabled = false;

            if(result)
            {
                var filename = this.value.replace(/\/$/, "").split("/");
                filename = filename[filename.length - 1] || "root";

                dump_file(result, filename);
                this.value = "";
            }
            else
            {
                alert("Can't read file");
            }
        };
    }

    function debug_start(emulator)
    {
        if(!emulator.v86)
        {
            return;
        }

        // called as soon as soon as emulation is started, in debug mode
        var debug = emulator.v86.cpu.debug;

        $("dump_gdt").onclick = debug.dump_gdt_ldt.bind(debug);
        $("dump_idt").onclick = debug.dump_idt.bind(debug);
        $("dump_regs").onclick = debug.dump_regs.bind(debug);
        $("dump_pt").onclick = debug.dump_page_structures.bind(debug);

        $("dump_log").onclick = function()
        {
            dump_file(log_data.join(""), "v86.log");
        };

        var cpu = emulator.v86.cpu;

        $("debug_panel").style.display = "block";
        setInterval(function()
        {
            $("debug_panel").textContent =
                cpu.debug.get_regs_short().join("\n") + "\n" + cpu.debug.get_state();

            $("dump_log").value = "Dump log" + (log_data.length ? " (" + log_data.length + " lines)" : "");
        }, 1000);

        // helps debugging
        window.emulator = emulator;
        window.cpu = cpu;
        window.dump_file = dump_file;
    }

    function onpopstate(e)
    {
        location.reload();
    }

    function set_profile(prof)
    {
        if(window.history.pushState)
        {
            window.history.pushState({ profile: prof }, "", "?profile=" + prof);
        }
    }

})();

<MSG> Replace page_poison=on with init_on_free=on

<DFF> @@ -203,7 +203,7 @@
                     "rw apm=off vga=0x344 video=vesafb:ypan,vremap:8",
                     "root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose",
                     "mitigations=off audit=0",
-                    "page_poison=on",
+                    "init_on_free=on",
                     "tsc=reliable",
                     "random.trust_cpu=on",
                     "nowatchdog",

{'commit': 'diff --git a/src/browser/main.js b/src/browser/main.js\nindex 054259a3..c8d789d5 100644\n--- a/src/browser/main.js\n+++ b/src/browser/main.js\n@@ -203,7 +203,7 @@\n                     "rw apm=off vga=0x344 video=vesafb:ypan,vremap:8",\n                     "root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose",\n                     "mitigations=off audit=0",\n-                    "page_poison=on",\n+                    "init_on_free=on",\n                     "tsc=reliable",\n                     "random.trust_cpu=on",\n                     "nowatchdog",\n', 'commit_subject': 'Replace page_poison=on with init_on_free=on', 'repo_name': 'copy/v86', 'committer': {'name': 'Fabian', 'email': 'f84e2e2dadd87384fb55f25886926b777e8378f1@copy.sh', 'time_sec': '1663047172', 'tz_offset': '540', 'date': {'seconds': '1663047172'}}, 'commit_tree': '7628084ccb00aab0ec3fd3049fda966b6de582b9', 'commit_message': 'Replace page_poison=on with init_on_free=on\n', 'language_name': 'JavaScript', 'license': 'bsd-2-clause', 'hunks': '@@ -203,7 +203,7 @@\n                     "rw apm=off vga=0x344 video=vesafb:ypan,vremap:8",\n                     "root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose",\n                     "mitigations=off audit=0",\n-                    "page_poison=on",\n+                    "init_on_free=on",\n                     "tsc=reliable",\n                     "random.trust_cpu=on",\n                     "nowatchdog",\n', 'addition_count': 1, 'deletion_count': 1, 'src_file': 'a/src/browser/main.js', 'tgt_file': 'b/src/browser/main.js', 'file_extension': '.js', 'before_file': '"use strict";\n\n(function()\n{\n    /** @const */\n    var ON_LOCALHOST = !location.hostname.endsWith("copy.sh");\n\n    /**\n     * @return {Object.<string, string>}\n     */\n    function get_query_arguments()\n    {\n        var query = location.search.substr(1).split("&");\n        var parameters = {};\n\n        for(var i = 0; i < query.length; i++)\n        {\n            var param = query[i].split("=");\n            parameters[param[0]] = decodeURIComponent(param.slice(1).join("="));\n        }\n\n        return parameters;\n    }\n\n    function set_title(text)\n    {\n        document.title = text + " - Virtual x86" +  (DEBUG ? " - debug" : "");\n        const description = document.querySelector("meta[name=description]");\n        description && (description.content = "Running " + text);\n    }\n\n    function format_timestamp(time)\n    {\n        if(time < 60)\n        {\n            return time + "s";\n        }\n        else if(time < 3600)\n        {\n            return (time / 60 | 0) + "m " + v86util.pad0(time % 60, 2) + "s";\n        }\n        else\n        {\n            return (time / 3600 | 0) + "h " +\n                v86util.pad0((time / 60 | 0) % 60, 2) + "m " +\n                v86util.pad0(time % 60, 2) + "s";\n        }\n    }\n\n    var progress_ticks = 0;\n\n    function show_progress(e)\n    {\n        var el = $("loading");\n        el.style.display = "block";\n\n        if(e.file_name.endsWith(".wasm"))\n        {\n            const parts = e.file_name.split("/");\n            el.textContent = "Fetching " + parts[parts.length - 1] + " ...";\n            return;\n        }\n\n        if(e.file_index === e.file_count - 1 && e.loaded >= e.total - 2048)\n        {\n            // last file is (almost) loaded\n            el.textContent = "Done downloading. Starting now ...";\n            return;\n        }\n\n        var line = "Downloading images ";\n\n        if(typeof e.file_index === "number" && e.file_count)\n        {\n            line += "[" + (e.file_index + 1) + "/" + e.file_count + "] ";\n        }\n\n        if(e.total && typeof e.loaded === "number")\n        {\n            var per100 = Math.floor(e.loaded / e.total * 100);\n            per100 = Math.min(100, Math.max(0, per100));\n\n            var per50 = Math.floor(per100 / 2);\n\n            line += per100 + "% [";\n            line += "#".repeat(per50);\n            line += " ".repeat(50 - per50) + "]";\n        }\n        else\n        {\n            line += ".".repeat(progress_ticks++ % 50);\n        }\n\n        el.textContent = line;\n    }\n\n    function $(id)\n    {\n        return document.getElementById(id);\n    }\n\n    function onload()\n    {\n        if(!window.WebAssembly)\n        {\n            alert("Your browser is not supported because it doesn\'t support WebAssembly");\n            return;\n        }\n\n        const script = document.createElement("script");\n        script.src = "build/xterm.js";\n        script.async = true;\n        document.body.appendChild(script);\n\n        var settings = {};\n\n        $("start_emulation").onclick = function()\n        {\n            $("boot_options").style.display = "none";\n            set_profile("custom");\n\n            var images = [];\n            var last_file;\n\n            var floppy_file = $("floppy_image").files[0];\n            if(floppy_file)\n            {\n                last_file = floppy_file;\n                settings.fda = { buffer: floppy_file };\n            }\n\n            var cd_file = $("cd_image").files[0];\n            if(cd_file)\n            {\n                last_file = cd_file;\n                settings.cdrom = { buffer: cd_file };\n            }\n\n            var hda_file = $("hda_image").files[0];\n            if(hda_file)\n            {\n                last_file = hda_file;\n                settings.hda = { buffer: hda_file };\n            }\n\n            var hdb_file = $("hdb_image") && $("hdb_image").files[0];\n            if(hdb_file)\n            {\n                last_file = hdb_file;\n                settings.hdb = { buffer: hdb_file };\n            }\n\n            if($("multiboot_image"))\n            {\n                var multiboot_file = $("multiboot_image").files[0];\n                if(multiboot_file)\n                {\n                    last_file = multiboot_file;\n                    settings.multiboot = { buffer: multiboot_file };\n                }\n            }\n\n            if(last_file)\n            {\n                set_title(last_file.name);\n            }\n\n            start_emulation(settings);\n        };\n\n        if(DEBUG)\n        {\n            debug_onload(settings);\n        }\n\n        const query_args = get_query_arguments();\n        const host = query_args["cdn"] || (ON_LOCALHOST ? "images/" : "//k.copy.sh/");\n\n        // Abandonware OS images are from https://winworldpc.com/library/operating-systems\n        var oses = [\n            {\n                id: "archlinux",\n                name: "Arch Linux",\n                memory_size: 512 * 1024 * 1024,\n                vga_memory_size: 8 * 1024 * 1024,\n                state: {\n                    url: host + "arch_state.bin.zst",\n                },\n                filesystem: {\n                    baseurl: host + "arch/",\n                },\n            },\n            {\n                id: "archlinux-boot",\n                name: "Arch Linux",\n                memory_size: 512 * 1024 * 1024,\n                vga_memory_size: 8 * 1024 * 1024,\n                filesystem: {\n                    baseurl: host + "arch/",\n                    basefs: { url: host + "fs.json", },\n                },\n                cmdline: [\n                    "rw apm=off vga=0x344 video=vesafb:ypan,vremap:8",\n                    "root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose",\n                    "mitigations=off audit=0",\n                    "page_poison=on",\n                    "tsc=reliable",\n                    "random.trust_cpu=on",\n                    "nowatchdog",\n                    "init=/usr/bin/init-openrc net.ifnames=0 biosdevname=0",\n                ].join(" "),\n                bzimage_initrd_from_filesystem: true,\n            },\n            {\n                id: "serenity",\n                name: "SerenityOS",\n                hda: {\n                    url: host + "serenity-v2.img",\n                    size: 700448768,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                memory_size: 512 * 1024 * 1024,\n                state: { url: host + "serenity_state-v3.bin.zst", },\n                homepage: "https://serenityos.org/",\n                mac_address_translation: true,\n            },\n            {\n                id: "serenity-boot",\n                name: "SerenityOS",\n                hda: {\n                    url: host + "serenity-v2.img",\n                    size: 700448768,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                memory_size: 512 * 1024 * 1024,\n                homepage: "https://serenityos.org/",\n            },\n            {\n                id: "serenity-old",\n                name: "SerenityOS",\n                hda: {\n                    url: host + "serenity.img",\n                    size: 876 * 1024 * 1024,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                memory_size: 512 * 1024 * 1024,\n                state: { url: host + "serenity_state-v2.bin.zst", },\n                homepage: "https://serenityos.org/",\n            },\n            {\n                id: "serenity-old-boot",\n                name: "SerenityOS",\n                hda: {\n                    url: host + "serenity.img",\n                    size: 876 * 1024 * 1024,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                memory_size: 512 * 1024 * 1024,\n                homepage: "https://serenityos.org/",\n            },\n            {\n                id: "helenos",\n                memory_size: 256 * 1024 * 1024,\n                cdrom: {\n                    url: host + "HelenOS-0.11.2-ia32.iso",\n                    size: 25765888,\n                    async: false,\n                },\n                name: "HelenOS",\n                homepage: "http://www.helenos.org/",\n            },\n            {\n                id: "haiku",\n                memory_size: 512 * 1024 * 1024,\n                hda: {\n                    url: host + "haiku-v2.img",\n                    size: 1 * 1024 * 1024 * 1024,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                state: {\n                    url: host + "haiku_state-v2.bin.zst",\n                },\n                name: "Haiku",\n                homepage: "https://www.haiku-os.org/",\n            },\n            {\n                id: "haiku-boot",\n                memory_size: 512 * 1024 * 1024,\n                hda: {\n                    url: host + "haiku-v2.img",\n                    size: 1 * 1024 * 1024 * 1024,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "Haiku",\n                homepage: "https://www.haiku-os.org/",\n            },\n            {\n                id: "msdos",\n                hda: {\n                    url: host + "msdos.img",\n                    size: 8 * 1024 * 1024,\n                    async: false,\n                },\n                boot_order: 0x132,\n                name: "MS-DOS",\n            },\n            {\n                id: "freedos",\n                fda: {\n                    url: host + "freedos722.img",\n                    size: 737280,\n                    async: false,\n                },\n                name: "FreeDOS",\n            },\n            {\n                id: "psychdos",\n                hda: {\n                    url: host + "psychdos.img",\n                    size: 549453824,\n                    async: true,\n                    fixed_chunk_size: 256 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "PsychDOS",\n                homepage: "https://psychoslinux.gitlab.io/DOS/INDEX.HTM",\n            },\n            {\n                id: "oberon",\n                hda: {\n                    url: host + "oberon.img",\n                    size: 24 * 1024 * 1024,\n                    async: false,\n                },\n                name: "Oberon",\n            },\n            {\n                id: "windows1",\n                fda: {\n                    url: host + "windows101.img",\n                    size: 1474560,\n                    async: false,\n                },\n                name: "Windows",\n            },\n            {\n                id: "linux26",\n                cdrom: {\n                    url: host + "linux.iso",\n                    size: 6547456,\n                    async: false,\n                },\n                name: "Linux",\n            },\n            {\n                id: "linux3",\n                cdrom: {\n                    url: host + "linux3.iso",\n                    size: 8624128,\n                    async: false,\n                },\n                name: "Linux",\n            },\n            {\n                id: "linux4",\n                cdrom: {\n                    url: host + "linux4.iso",\n                    size: 7731200,\n                    async: false,\n                },\n                name: "Linux",\n                filesystem: {},\n            },\n            {\n                id: "buildroot",\n                bzimage: {\n                    url: host + "buildroot-bzimage.bin",\n                    size: 5166352,\n                    async: false,\n                },\n                name: "Buildroot Linux",\n                filesystem: {},\n                cmdline: "tsc=reliable mitigations=off random.trust_cpu=on",\n            },\n            {\n                id: "nodeos",\n                bzimage: {\n                    url: host + "nodeos-kernel.bin",\n                    size: 14452000,\n                    async: false,\n                },\n                name: "NodeOS",\n                cmdline: "tsc=reliable mitigations=off random.trust_cpu=on",\n            },\n            {\n                id: "dsl",\n                memory_size: 256 * 1024 * 1024,\n                cdrom: {\n                    url: host + "dsl-4.11.rc2.iso",\n                    size: 52824064,\n                    async: false,\n                },\n                name: "Damn Small Linux",\n                homepage: "http://www.damnsmalllinux.org/",\n            },\n            {\n                id: "minix",\n                name: "Minix",\n                memory_size: 256 * 1024 * 1024,\n                cdrom: {\n                    url: host + "minix-3.3.0.iso",\n                    size: 605581312,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                homepage: "https://www.minix3.org/",\n            },\n            {\n                id: "kolibrios",\n                fda: {\n                    url: ON_LOCALHOST ?\n                            host + "kolibri.img" :\n                            "//builds.kolibrios.org/eng/data/data/kolibri.img",\n                    size: 1474560,\n                    async: false,\n                },\n                name: "KolibriOS",\n                homepage: "https://kolibrios.org/en/",\n            },\n            {\n                id: "kolibrios-fallback",\n                fda: {\n                    url: host + "kolibri.img",\n                    size: 1474560,\n                    async: false,\n                },\n                name: "KolibriOS",\n            },\n            {\n                id: "openbsd",\n                hda: {\n                    url: host + "openbsd.img",\n                    size: 1073741824,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                state: {\n                    url: host + "openbsd_state.bin.zst",\n                },\n                memory_size: 256 * 1024 * 1024,\n                name: "OpenBSD",\n            },\n            {\n                id: "openbsd-boot",\n                hda: {\n                    url: host + "openbsd.img",\n                    size: 1073741824,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                memory_size: 256 * 1024 * 1024,\n                name: "OpenBSD",\n                //acpi: true, // doesn\'t seem to work\n            },\n            {\n                id: "netbsd",\n                hda: {\n                    url: host + "netbsd.img",\n                    size: 511000064,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                memory_size: 256 * 1024 * 1024,\n                name: "NetBSD",\n            },\n            {\n                id: "solos",\n                fda: {\n                    url: host + "os8.img",\n                    async: false,\n                    size: 1474560,\n                },\n                name: "Sol OS",\n                homepage: "http://oby.ro/os/",\n            },\n            {\n                id: "bootchess",\n                fda: {\n                    url: host + "bootchess.img",\n                    async: false,\n                    size: 1474560,\n                },\n                name: "BootChess",\n                homepage: "http://www.pouet.net/prod.php?which=64962",\n            },\n            {\n                id: "bootbasic",\n                fda: {\n                    url: host + "bootbasic.img",\n                    async: false,\n                    size: 1474560,\n                },\n                name: "bootBASIC",\n                homepage: "https://github.com/nanochess/bootBASIC",\n            },\n            {\n                id: "sectorlisp",\n                fda: {\n                    url: host + "sectorlisp-friendly.bin",\n                    async: false,\n                    size: 512,\n                },\n                name: "SectorLISP",\n                homepage: "https://justine.lol/sectorlisp2/",\n            },\n            {\n                id: "sectorforth",\n                fda: {\n                    url: host + "sectorforth.img",\n                    async: false,\n                    size: 512,\n                },\n                name: "sectorforth",\n                homepage: "https://github.com/cesarblum/sectorforth",\n            },\n            {\n                id: "floppybird",\n                fda: {\n                    url: host + "floppybird.img",\n                    async: false,\n                    size: 1474560,\n                },\n                name: "Floppy Bird",\n                homepage: "http://mihail.co/floppybird",\n            },\n            {\n                id: "windows2000",\n                memory_size: 512 * 1024 * 1024,\n                hda: {\n                    url: host + "windows2k.img",\n                    size: 2 * 1024 * 1024 * 1024,\n                    async: true,\n                    fixed_chunk_size: 256 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "Windows 2000",\n                state: {\n                    url: host + "windows2k_state-v2.bin.zst",\n                },\n                mac_address_translation: true,\n            },\n            {\n                id: "windows2000-boot",\n                memory_size: 512 * 1024 * 1024,\n                hda: {\n                    url: host + "windows2k.img",\n                    size: 2 * 1024 * 1024 * 1024,\n                    async: true,\n                    fixed_chunk_size: 256 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                boot_order: 0x132,\n                name: "Windows 2000",\n            },\n            {\n                id: "windowsnt4",\n                memory_size: 512 * 1024 * 1024,\n                hda: {\n                    url: host + "winnt4_noacpi.img",\n                    size: 523837440,\n                    async: true,\n                    fixed_chunk_size: 256 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "Windows NT 4.0",\n                cpuid_level: 2,\n            },\n            {\n                id: "windowsnt3",\n                memory_size: 256 * 1024 * 1024,\n                hda: {\n                    url: host + "winnt31.img",\n                    size: 87 * 1024 * 1024,\n                    async: true,\n                    fixed_chunk_size: 256 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "Windows NT 3.1",\n            },\n            {\n                id: "windows98",\n                memory_size: 128 * 1024 * 1024,\n                hda: {\n                    url: host + "windows98.img",\n                    size: 300 * 1024 * 1024,\n                    async: true,\n                    fixed_chunk_size: 256 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "Windows 98",\n                state: {\n                    url: host + "windows98_state.bin.zst",\n                },\n                mac_address_translation: true,\n            },\n            {\n                id: "windows98-boot",\n                memory_size: 128 * 1024 * 1024,\n                hda: {\n                    url: host + "windows98.img",\n                    size: 300 * 1024 * 1024,\n                    async: true,\n                    fixed_chunk_size: 256 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "Windows 98",\n            },\n            {\n                id: "windows95",\n                memory_size: 32 * 1024 * 1024,\n                hda: {\n                    url: host + "w95.img",\n                    size: 242049024,\n                    async: true,\n                    fixed_chunk_size: 256 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "Windows 95",\n                state: {\n                    url: host + "windows95_state.bin.zst",\n                },\n            },\n            {\n                id: "windows95-boot",\n                memory_size: 32 * 1024 * 1024,\n                hda: {\n                    url: host + "w95.img",\n                    size: 242049024,\n                    async: true,\n                    fixed_chunk_size: 256 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "Windows 95",\n            },\n            {\n                id: "windows30",\n                memory_size: 64 * 1024 * 1024,\n                cdrom: {\n                    url: host + "Win30.iso",\n                    async: false,\n                },\n                name: "Windows 3.0",\n            },\n            {\n                id: "windows31",\n                memory_size: 64 * 1024 * 1024,\n                hda: {\n                    url: host + "win31.img",\n                    async: false,\n                    size: 34463744,\n                },\n                name: "Windows 3.1",\n            },\n            {\n                id: "freebsd",\n                memory_size: 256 * 1024 * 1024,\n                hda: {\n                    url: host + "freebsd.img",\n                    size: 2147483648,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                state: {\n                    url: host + "freebsd_state.bin.zst",\n                },\n                name: "FreeBSD",\n            },\n            {\n                id: "freebsd-boot",\n                memory_size: 256 * 1024 * 1024,\n                hda: {\n                    url: host + "freebsd.img",\n                    size: 2147483648,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "FreeBSD",\n            },\n            {\n                id: "reactos-livecd",\n                memory_size: 256 * 1024 * 1024,\n                hda: {\n                    url: host + "reactos-livecd-0.4.15-dev-73-g03c09c9-x86-gcc-lin-dbg.iso",\n                    size: 250609664,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "ReactOS",\n                homepage: "https://reactos.org/",\n            },\n            {\n                id: "reactos",\n                memory_size: 512 * 1024 * 1024,\n                hda: {\n                    url: host + "reactos.img",\n                    size: 500 * 1024 * 1024,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                state: {\n                    url: host + "reactos_state.bin.zst",\n                },\n                mac_address_translation: true,\n                name: "ReactOS",\n                homepage: "https://reactos.org/",\n            },\n            {\n                id: "reactos-boot",\n                memory_size: 512 * 1024 * 1024,\n                hda: {\n                    url: host + "reactos.img",\n                    size: 500 * 1024 * 1024,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "ReactOS",\n                homepage: "https://reactos.org/",\n            },\n            {\n                id: "skift",\n                cdrom: {\n                    url: host + "skift-20200910.iso",\n                    size: 64452608,\n                    async: false,\n                },\n                name: "Skift",\n                homepage: "https://skiftos.org/",\n            },\n            {\n                id: "snowdrop",\n                fda: {\n                    url: host + "snowdrop.img",\n                    size: 1440 * 1024,\n                    async: false,\n                },\n                name: "Snowdrop",\n                homepage: "http://www.sebastianmihai.com/snowdrop/",\n            },\n            {\n                id: "openwrt",\n                hda: {\n                    url: host + "openwrt-18.06.1-x86-legacy-combined-squashfs.img",\n                    size: 19846474,\n                    async: false,\n                },\n                name: "OpenWrt",\n            },\n            {\n                id: "qnx",\n                fda: {\n                    url: host + "qnx-demo-network-4.05.img",\n                    size: 1474560,\n                    async: false\n                },\n                name: "QNX 4.05",\n            },\n            {\n                id: "9front",\n                memory_size: 128 * 1024 * 1024,\n                hda: {\n                    url: host + "9front-8963.f84cf1e60427675514fb056cc1723e45da01e043.386.iso",\n                    size: 477452288,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                state: {\n                    url: host + "9front_state-v2.bin.zst",\n                },\n                acpi: true,\n                name: "9front",\n                homepage: "https://9front.org/",\n            },\n            {\n                id: "9front-boot",\n                memory_size: 128 * 1024 * 1024,\n                hda: {\n                    url: host + "9front-8963.f84cf1e60427675514fb056cc1723e45da01e043.386.iso",\n                    size: 477452288,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                acpi: true,\n                name: "9front",\n                homepage: "https://9front.org/",\n            },\n            {\n                id: "mobius",\n                fda: {\n                    url: host + "mobius-fd-release5.img",\n                    size: 1474560,\n                    async: false,\n                },\n                name: "Mobius",\n            },\n            {\n                id: "android",\n                memory_size: 512 * 1024 * 1024,\n                cdrom: {\n                    url: host + "android-x86-1.6-r2.iso",\n                    size: 54661120,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "Android",\n            },\n            {\n                id: "android4",\n                memory_size: 512 * 1024 * 1024,\n                cdrom: {\n                    url: host + "android_x86_nonsse3_4.4r1_20140904.iso",\n                    size: 247463936,\n                    async: true,\n                    fixed_chunk_size: 1024 * 1024,\n                    use_parts: !ON_LOCALHOST,\n                },\n                name: "Android",\n            },\n            {\n                id: "tinycore",\n                memory_size: 256 * 1024 * 1024,\n                hda: {\n                    url: host + "TinyCore-11.0.iso",\n                    async: false,\n                },\n                name: "Tinycore",\n                homepage: "http://www.tinycorelinux.net/",\n            },\n            {\n                id: "freenos",\n                memory_size: 256 * 1024 * 1024,\n                cdrom: {\n                    url: host + "FreeNOS-1.0.3.iso",\n                    async: false,\n                    size: 11014144,\n                },\n                name: "FreeNOS",\n                acpi: true,\n                homepage: "http://www.freenos.org/",\n            },\n        ];\n\n        if(DEBUG)\n        {\n            // see tests/kvm-unit-tests/x86/\n            var tests = [\n                "realmode",\n                // All tests below require an APIC\n                "cmpxchg8b",\n                "port80",\n                "setjmp",\n                "sieve",\n                "hypercall", // crashes\n                "init", // stops execution\n                "msr", // TODO: Expects 64 bit msrs\n                "smap", // test stops, SMAP not enabled\n                "tsc_adjust", // TODO: IA32_TSC_ADJUST\n                "tsc", // TODO: rdtscp\n                "rmap_chain", // crashes\n                "memory", // missing mfence (uninteresting)\n                "taskswitch", // TODO: Jump\n                "taskswitch2", // TODO: Call TSS\n                "eventinj", // Missing #nt\n                "ioapic",\n                "apic",\n            ];\n\n            for(let test of tests)\n            {\n                oses.push({\n                    name: "Test case: " + test,\n                    id: "test-" + test,\n                    memory_size: 128 * 1024 * 1024,\n                    multiboot: { url: "tests/kvm-unit-tests/x86/" + test + ".flat", }\n                });\n            }\n        }\n\n        var profile = query_args["profile"];\n\n        if(!profile && !DEBUG)\n        {\n            const link = document.createElement("link");\n            link.rel = "prefetch";\n            link.href = "build/v86.wasm";\n            document.head.appendChild(link);\n        }\n\n        if(query_args["use_bochs_bios"])\n        {\n            settings.use_bochs_bios = true;\n        }\n\n        const m = parseInt(query_args["m"], 10);\n        if(m > 0)\n        {\n            settings.memory_size = Math.max(16, m) * 1024 * 1024;\n        }\n\n        const vram = parseInt(query_args["vram"], 10);\n        if(vram > 0)\n        {\n            settings.vga_memory_size = vram * 1024 * 1024;\n        }\n\n        settings.networking_proxy = query_args["networking_proxy"];\n        settings.audio = query_args["audio"] !== "0";\n        settings.acpi = query_args["acpi"];\n\n        for(var i = 0; i < oses.length; i++)\n        {\n            var infos = oses[i];\n\n            if(profile === infos.id)\n            {\n                start_profile(infos);\n                return;\n            }\n\n            var element = $("start_" + infos.id);\n\n            if(element)\n            {\n                element.onclick = function(infos, element, e)\n                {\n                    e.preventDefault();\n                    set_profile(infos.id);\n                    element.blur();\n\n                    start_profile(infos);\n                }.bind(this, infos, element);\n            }\n        }\n\n        if(profile === "custom")\n        {\n            if(query_args["hda.url"])\n            {\n                settings.hda = {\n                    size: parseInt(query_args["hda.size"], 10) || undefined,\n                    url: query_args["hda.url"],\n                    async: true,\n                };\n            }\n\n            if(query_args["cdrom.url"])\n            {\n                settings.cdrom = {\n                    size: parseInt(query_args["cdrom.size"], 10) || undefined,\n                    url: query_args["cdrom.url"],\n                    async: true,\n                };\n            }\n\n            if(query_args["fda.url"])\n            {\n                settings.fda = {\n                    size: parseInt(query_args["fda.size"], 10) || undefined,\n                    url: query_args["fda.url"],\n                    async: false,\n                };\n            }\n\n            if(settings.fda || settings.cdrom || settings.hda)\n            {\n                $("boot_options").style.display = "none";\n\n                start_emulation(settings, done);\n            }\n        }\n        else if(/^[a-zA-Z0-9\\-_]+\\/[a-zA-Z0-9\\-_]+$/g.test(profile))\n        {\n            // experimental: server that allows user-uploaded images\n\n            const base = "https://v86-user-images.b-cdn.net/" + profile;\n\n            fetch(base + "/profile.json")\n                .then(response => response.json())\n                .then(p => {\n                    function handle_image(o)\n                    {\n                        return o && { url: base + "/" + o["url"], async: o["async"], size: o["size"] };\n                    }\n\n                    start_profile({\n                        id: p["id"],\n                        name: p["name"],\n                        memory_size: p["memory_size"],\n                        vga_memory_size: p["vga_memory_size"],\n                        acpi: p["acpi"],\n                        boot_order: p["boot_order"],\n                        hda: handle_image(p["hda"]),\n                        cdrom: handle_image(p["cdrom"]),\n                        fda: handle_image(p["fda"]),\n                        multiboot: handle_image(p["multiboot"]),\n                        bzimage: handle_image(p["bzimage"]),\n                        initrd: handle_image(p["initrd"]),\n                    });\n                })\n                .catch(e => alert("Profile not found: " + profile));\n        }\n\n        function start_profile(infos)\n        {\n            $("boot_options").style.display = "none";\n            set_title(infos.name);\n\n            settings.filesystem = infos.filesystem;\n\n            if(infos.state)\n            {\n                $("reset").style.display = "none";\n                settings.initial_state = infos.state;\n            }\n\n            settings.fda = infos.fda;\n            settings.cdrom = infos.cdrom;\n            settings.hda = infos.hda;\n            settings.multiboot = infos.multiboot;\n            settings.bzimage = infos.bzimage;\n            settings.initrd = infos.initrd;\n            settings.cmdline = infos.cmdline;\n            settings.bzimage_initrd_from_filesystem = infos.bzimage_initrd_from_filesystem;\n            settings.mac_address_translation = infos.mac_address_translation;\n            settings.cpuid_level = infos.cpuid_level;\n\n            settings.acpi = (!infos.state && settings.acpi !== undefined) ? settings.acpi : infos.acpi;\n            settings.memory_size = (!infos.state && settings.memory_size) ? settings.memory_size : infos.memory_size;\n            settings.vga_memory_size = (!infos.state && settings.vga_memory_size) ? settings.vga_memory_size : infos.vga_memory_size;\n\n            settings.id = infos.id;\n\n            if(infos.boot_order !== undefined)\n            {\n                settings.boot_order = infos.boot_order;\n            }\n\n            let chunk_size = parseInt(query_args["chunk_size"], 10);\n            if(chunk_size >= 0)\n            {\n                if(chunk_size)\n                {\n                    chunk_size = Math.min(4 * 1024 * 1024, Math.max(512, chunk_size));\n                    chunk_size = 1 << Math.ceil(Math.log2(chunk_size));\n                }\n                else\n                {\n                    chunk_size = undefined;\n                }\n\n                if(settings.hda)\n                {\n                    settings.hda.fixed_chunk_size = chunk_size;\n                }\n\n                if(settings.cdrom)\n                {\n                    settings.cdrom.fixed_chunk_size = chunk_size;\n                }\n            }\n\n            if(!DEBUG && infos.homepage)\n            {\n                $("description").style.display = "block";\n                const link = document.createElement("a");\n                link.href = infos.homepage;\n                link.textContent = infos.name;\n                link.target = "_blank";\n                $("description").appendChild(document.createTextNode("Running "));\n                $("description").appendChild(link);\n            }\n\n            start_emulation(settings, done);\n        }\n\n        function done(emulator)\n        {\n            if(query_args["c"])\n            {\n                setTimeout(function()\n                {\n                    //emulator.serial0_send(query_args["c"] + "\\n");\n                    emulator.keyboard_send_text(query_args["c"] + "\\n");\n                }, 25);\n            }\n        }\n    }\n\n    function debug_onload(settings)\n    {\n        // called on window.onload, in debug mode\n\n        var log_levels = $("log_levels");\n\n        if(log_levels)\n        {\n            for(var i = 0; i < LOG_NAMES.length; i++)\n            {\n                var mask = LOG_NAMES[i][0];\n\n                if(mask === 1)\n                    continue;\n\n                var name = LOG_NAMES[i][1].toLowerCase(),\n                    input = document.createElement("input"),\n                    label = document.createElement("label");\n\n                input.type = "checkbox";\n\n                label.htmlFor = input.id = "log_" + name;\n\n                if(LOG_LEVEL & mask)\n                {\n                    input.checked = true;\n                }\n                input.mask = mask;\n\n                label.appendChild(input);\n                label.appendChild(document.createTextNode(v86util.pads(name, 4) + " "));\n                log_levels.appendChild(label);\n\n                if(i === Math.floor(LOG_NAMES.length / 2))\n                {\n                    log_levels.appendChild(document.createTextNode("\\n"));\n                }\n            }\n\n            log_levels.onchange = function(e)\n            {\n                var target = e.target,\n                    mask = target.mask;\n\n                if(target.checked)\n                {\n                    LOG_LEVEL |= mask;\n                }\n                else\n                {\n                    LOG_LEVEL &= ~mask;\n                }\n\n                target.blur();\n            };\n        }\n    }\n\n    window.addEventListener("load", onload, false);\n\n    // old webkit fires popstate on every load, fuck webkit\n    // https://code.google.com/p/chromium/issues/detail?id=63040\n    window.addEventListener("load", function()\n    {\n        setTimeout(function()\n        {\n            window.addEventListener("popstate", onpopstate);\n        }, 0);\n    });\n\n    // works in firefox and chromium\n    if(document.readyState === "complete")\n    {\n        onload();\n    }\n\n    /** @param {?=} done */\n    function start_emulation(settings, done)\n    {\n        /** @const */\n        var MB = 1024 * 1024;\n\n        var memory_size = settings.memory_size;\n\n        if(!memory_size)\n        {\n            memory_size = parseInt($("memory_size").value, 10) * MB;\n\n            if(!memory_size)\n            {\n                alert("Invalid memory size - reset to 128MB");\n                memory_size = 128 * MB;\n            }\n        }\n\n        var vga_memory_size = settings.vga_memory_size;\n\n        if(!vga_memory_size)\n        {\n            vga_memory_size = parseInt($("video_memory_size").value, 10) * MB;\n\n            if(!vga_memory_size)\n            {\n                alert("Invalid video memory size - reset to 8MB");\n                vga_memory_size = 8 * MB;\n            }\n        }\n\n        if(!settings.fda)\n        {\n            var floppy_file = $("floppy_image").files[0];\n            if(floppy_file)\n            {\n                settings.fda = { buffer: floppy_file };\n            }\n        }\n\n        if(!settings.bzimage)\n        {\n            var bzimage = $("bzimage").files[0];\n            if(bzimage)\n            {\n                settings.bzimage = { buffer: bzimage };\n            }\n        }\n\n        if(!settings.initrd)\n        {\n            var initrd = $("initrd").files[0];\n            if(initrd)\n            {\n                settings.initrd = { buffer: initrd };\n            }\n        }\n\n        const networking_proxy = settings.networking_proxy === undefined ? $("networking_proxy").value : settings.networking_proxy;\n        const disable_audio = settings.audio === undefined ? $("disable_audio").checked : !settings.audio;\n        const enable_acpi = settings.acpi === undefined ? $("enable_acpi").checked : settings.acpi;\n\n        /** @const */\n        var BIOSPATH = "bios/";\n\n        if(settings.use_bochs_bios)\n        {\n            var biosfile = "bochs-bios.bin";\n            var vgabiosfile = "bochs-vgabios.bin";\n        }\n        else\n        {\n            var biosfile = DEBUG ? "seabios-debug.bin" : "seabios.bin";\n            var vgabiosfile = DEBUG ? "vgabios-debug.bin" : "vgabios.bin";\n        }\n\n        var bios;\n        var vga_bios;\n\n        // a bios is only needed if the machine is booted\n        if(!settings.initial_state)\n        {\n            bios = {\n                url: BIOSPATH + biosfile,\n            };\n            vga_bios = {\n                url: BIOSPATH + vgabiosfile,\n            };\n        }\n\n        var emulator = new V86Starter({\n            "memory_size": memory_size,\n            "vga_memory_size": vga_memory_size,\n\n            "screen_container": $("screen_container"),\n            "serial_container_xtermjs": $("terminal"),\n\n            "boot_order": settings.boot_order || parseInt($("boot_order").value, 16) || 0,\n\n            "network_relay_url": ON_LOCALHOST ? "ws://localhost:8080/" : networking_proxy,\n\n            "bios": bios,\n            "vga_bios": vga_bios,\n\n            "fda": settings.fda,\n            "hda": settings.hda,\n            "hdb": settings.hdb,\n            "cdrom": settings.cdrom,\n\n            "multiboot": settings.multiboot,\n            "bzimage": settings.bzimage,\n            "initrd": settings.initrd,\n            "cmdline": settings.cmdline,\n            "bzimage_initrd_from_filesystem": settings.bzimage_initrd_from_filesystem,\n\n            "acpi": enable_acpi,\n            "initial_state": settings.initial_state,\n            "filesystem": settings.filesystem || {},\n            "disable_speaker": disable_audio,\n            "mac_address_translation": settings.mac_address_translation,\n            "cpuid_level": settings.cpuid_level,\n\n            "autostart": true,\n        });\n\n        if(DEBUG) window["emulator"] = emulator;\n\n        emulator.add_listener("emulator-ready", function()\n        {\n            if(DEBUG)\n            {\n                debug_start(emulator);\n            }\n\n            if(emulator.v86.cpu.wm.exports["profiler_is_enabled"]())\n            {\n                const CLEAR_STATS = false;\n\n                var panel = document.createElement("pre");\n                document.body.appendChild(panel);\n\n                setInterval(function()\n                    {\n                        if(!emulator.is_running())\n                        {\n                            return;\n                        }\n\n                        const text = print_stats.stats_to_string(emulator.v86.cpu);\n                        panel.textContent = text;\n\n                        CLEAR_STATS && emulator.v86.cpu.clear_opstats();\n                    }, CLEAR_STATS ? 5000 : 1000);\n            }\n\n            if(settings.id === "dsl" || settings.id === "helenos")\n            {\n                setTimeout(() => {\n                    // hack: Start automatically\n                    emulator.keyboard_send_text("\\n");\n                }, 3000);\n            }\n            else if(settings.id === "android" || settings.id === "android4")\n            {\n                setTimeout(() => {\n                    // hack: select vesa mode and start automatically\n                    emulator.keyboard_send_scancodes([0xe050, 0xe050 | 0x80]);\n                    emulator.keyboard_send_text("\\n");\n                }, 3000);\n            }\n\n            init_ui(settings, emulator);\n\n            done && done(emulator);\n        });\n\n        emulator.add_listener("download-progress", function(e)\n        {\n            show_progress(e);\n        });\n\n        emulator.add_listener("download-error", function(e)\n        {\n            var el = $("loading");\n            el.style.display = "block";\n            el.textContent = "Loading " + e.file_name + " failed. Check your connection " +\n                             "and reload the page to try again.";\n        });\n    }\n\n    /**\n     * @param {Object} settings\n     * @param {V86Starter} emulator\n     */\n    function init_ui(settings, emulator)\n    {\n        $("boot_options").style.display = "none";\n        $("loading").style.display = "none";\n        $("runtime_options").style.display = "block";\n        $("runtime_infos").style.display = "block";\n        $("screen_container").style.display = "block";\n\n        if(settings.filesystem)\n        {\n            init_filesystem_panel(emulator);\n        }\n        else\n        {\n            emulator.add_listener("9p-attach", function()\n            {\n                init_filesystem_panel(emulator);\n            });\n        }\n\n        $("run").onclick = function()\n        {\n            if(emulator.is_running())\n            {\n                $("run").value = "Run";\n                emulator.stop();\n            }\n            else\n            {\n                $("run").value = "Pause";\n                emulator.run();\n            }\n\n            $("run").blur();\n        };\n\n        $("exit").onclick = function()\n        {\n            emulator.stop();\n            location.href = location.pathname;\n        };\n\n        $("lock_mouse").onclick = function()\n        {\n            if(!mouse_is_enabled)\n            {\n                $("toggle_mouse").onclick();\n            }\n\n            emulator.lock_mouse();\n            $("lock_mouse").blur();\n        };\n\n        var mouse_is_enabled = true;\n\n        $("toggle_mouse").onclick = function()\n        {\n            mouse_is_enabled = !mouse_is_enabled;\n\n            emulator.mouse_set_status(mouse_is_enabled);\n            $("toggle_mouse").value = (mouse_is_enabled ? "Dis" : "En") + "able mouse";\n            $("toggle_mouse").blur();\n        };\n\n\n        var last_tick = 0;\n        var running_time = 0;\n        var last_instr_counter = 0;\n        var interval = null;\n        var os_uses_mouse = false;\n        var total_instructions = 0;\n\n        function update_info()\n        {\n            var now = Date.now();\n\n            var instruction_counter = emulator.get_instruction_counter();\n\n            if(instruction_counter < last_instr_counter)\n            {\n                // 32-bit wrap-around\n                last_instr_counter -= 0x100000000;\n            }\n\n            var last_ips = instruction_counter - last_instr_counter;\n            last_instr_counter = instruction_counter;\n            total_instructions += last_ips;\n\n            var delta_time = now - last_tick;\n\n            if(delta_time)\n            {\n                running_time += delta_time;\n                last_tick = now;\n\n                $("speed").textContent = (last_ips / 1000 / delta_time).toFixed(1);\n                $("avg_speed").textContent = (total_instructions / 1000 / running_time).toFixed(1);\n                $("running_time").textContent = format_timestamp(running_time / 1000 | 0);\n            }\n        }\n\n        emulator.add_listener("emulator-started", function()\n        {\n            last_tick = Date.now();\n            interval = setInterval(update_info, 1000);\n        });\n\n        emulator.add_listener("emulator-stopped", function()\n        {\n            update_info();\n            if(interval !== null)\n            {\n                clearInterval(interval);\n            }\n        });\n\n        var stats_9p = {\n            read: 0,\n            write: 0,\n            files: [],\n        };\n\n        emulator.add_listener("9p-read-start", function(args)\n        {\n            const file = args[0];\n            stats_9p.files.push(file);\n            $("info_filesystem").style.display = "block";\n            $("info_filesystem_status").textContent = "Loading ...";\n            $("info_filesystem_last_file").textContent = file;\n        });\n        emulator.add_listener("9p-read-end", function(args)\n        {\n            stats_9p.read += args[1];\n            $("info_filesystem_bytes_read").textContent = stats_9p.read;\n\n            const file = args[0];\n            stats_9p.files = stats_9p.files.filter(f => f !== file);\n\n            if(stats_9p.files[0])\n            {\n                $("info_filesystem_last_file").textContent = stats_9p.files[0];\n            }\n            else\n            {\n                $("info_filesystem_status").textContent = "Idle";\n            }\n        });\n        emulator.add_listener("9p-write-end", function(args)\n        {\n            stats_9p.write += args[1];\n            $("info_filesystem_bytes_written").textContent = stats_9p.write;\n\n            if(!stats_9p.files[0])\n            {\n                $("info_filesystem_last_file").textContent = args[0];\n            }\n        });\n\n        var stats_storage = {\n            read: 0,\n            read_sectors: 0,\n            write: 0,\n            write_sectors: 0,\n        };\n\n        emulator.add_listener("ide-read-start", function()\n        {\n            $("info_storage").style.display = "block";\n            $("info_storage_status").textContent = "Loading ...";\n        });\n        emulator.add_listener("ide-read-end", function(args)\n        {\n            stats_storage.read += args[1];\n            stats_storage.read_sectors += args[2];\n\n            $("info_storage_status").textContent = "Idle";\n            $("info_storage_bytes_read").textContent = stats_storage.read;\n            $("info_storage_sectors_read").textContent = stats_storage.read_sectors;\n        });\n        emulator.add_listener("ide-write-end", function(args)\n        {\n            stats_storage.write += args[1];\n            stats_storage.write_sectors += args[2];\n\n            $("info_storage_bytes_written").textContent = stats_storage.write;\n            $("info_storage_sectors_written").textContent = stats_storage.write_sectors;\n        });\n\n        var stats_net = {\n            bytes_transmitted: 0,\n            bytes_received: 0,\n        };\n\n        emulator.add_listener("eth-receive-end", function(args)\n        {\n            stats_net.bytes_received += args[0];\n\n            $("info_network").style.display = "block";\n            $("info_network_bytes_received").textContent = stats_net.bytes_received;\n        });\n        emulator.add_listener("eth-transmit-end", function(args)\n        {\n            stats_net.bytes_transmitted += args[0];\n\n            $("info_network").style.display = "block";\n            $("info_network_bytes_transmitted").textContent = stats_net.bytes_transmitted;\n        });\n\n\n        emulator.add_listener("mouse-enable", function(is_enabled)\n        {\n            os_uses_mouse = is_enabled;\n            $("info_mouse_enabled").textContent = is_enabled ? "Yes" : "No";\n        });\n\n        emulator.add_listener("screen-set-mode", function(is_graphical)\n        {\n            if(is_graphical)\n            {\n                $("info_vga_mode").textContent = "Graphical";\n            }\n            else\n            {\n                $("info_vga_mode").textContent = "Text";\n                $("info_res").textContent = "-";\n                $("info_bpp").textContent = "-";\n            }\n        });\n        emulator.add_listener("screen-set-size-graphical", function(args)\n        {\n            $("info_res").textContent = args[0] + "x" + args[1];\n            $("info_bpp").textContent = args[4];\n        });\n\n\n        $("reset").onclick = function()\n        {\n            emulator.restart();\n            $("reset").blur();\n        };\n\n        add_image_download_button(settings.hda, "hda");\n        add_image_download_button(settings.hdb, "hdb");\n        add_image_download_button(settings.fda, "fda");\n        add_image_download_button(settings.fdb, "fdb");\n        add_image_download_button(settings.cdrom, "cdrom");\n\n        function add_image_download_button(obj, type)\n        {\n            var elem = $("get_" + type + "_image");\n\n            if(!obj || obj.size > 100 * 1024 * 1024)\n            {\n                elem.style.display = "none";\n                return;\n            }\n\n            elem.onclick = function(e)\n            {\n                let buffer = emulator.disk_images[type];\n                let filename = buffer.file && buffer.file.name || (settings.id + (type === "cdrom" ? ".iso" : ".img"));\n\n                if(buffer.get_as_file)\n                {\n                    var file = buffer.get_as_file(filename);\n                    download(file, filename);\n                }\n                else\n                {\n                    buffer.get_buffer(function(b)\n                    {\n                        if(b)\n                        {\n                            dump_file(b, filename);\n                        }\n                        else\n                        {\n                            alert("The file could not be loaded. Maybe it\'s too big?");\n                        }\n                    });\n                }\n\n                elem.blur();\n            };\n        }\n\n        $("memory_dump").onclick = function()\n        {\n            const mem8 = emulator.v86.cpu.mem8;\n            dump_file(new Uint8Array(mem8.buffer, mem8.byteOffset, mem8.length), "v86memory.bin");\n            $("memory_dump").blur();\n        };\n\n        //$("memory_dump_dmp").onclick = function()\n        //{\n        //    var memory = emulator.v86.cpu.mem8;\n        //    var memory_size = memory.length;\n        //    var page_size = 4096;\n        //    var header = new Uint8Array(4096);\n        //    var header32 = new Int32Array(header.buffer);\n\n        //    header32[0] = 0x45474150; // \'PAGE\'\n        //    header32[1] = 0x504D5544; // \'DUMP\'\n\n        //    header32[0x10 >> 2] = emulator.v86.cpu.cr[3]; // DirectoryTableBase\n        //    header32[0x24 >> 2] = 1; // NumberProcessors\n        //    header32[0xf88 >> 2] = 1; // DumpType: full dump\n        //    header32[0xfa0 >> 2] = header.length + memory_size; // RequiredDumpSpace\n\n        //    header32[0x064 + 0 >> 2] = 1; // NumberOfRuns\n        //    header32[0x064 + 4 >> 2] = memory_size / page_size; // NumberOfPages\n        //    header32[0x064 + 8 >> 2] = 0; // BasePage\n        //    header32[0x064 + 12 >> 2] = memory_size / page_size; // PageCount\n\n        //    dump_file([header, memory], "v86memory.dmp");\n\n        //    $("memory_dump_dmp").blur();\n        //};\n\n        $("capture_network_traffic").onclick = function()\n        {\n            this.value = "0 packets";\n\n            let capture = [];\n\n            function do_capture(direction, data)\n            {\n                capture.push({ direction, time: performance.now() / 1000, hex_dump: hex_dump(data) });\n                $("capture_network_traffic").value = capture.length + " packets";\n            }\n\n            emulator.emulator_bus.register("net0-receive", do_capture.bind(this, "I"));\n            emulator.add_listener("net0-send", do_capture.bind(this, "O"));\n\n            this.onclick = function()\n            {\n                const capture_raw = capture.map(({ direction, time, hex_dump }) => {\n                    // https://www.wireshark.org/docs/wsug_html_chunked/ChIOImportSection.html\n                    // In wireshark: file -> import from hex -> tick direction indication, timestamp %s.%f\n                    return direction + " " + time.toFixed(6) + hex_dump + "\\n";\n                }).join("");\n                dump_file(capture_raw, "traffic.hex");\n                capture = [];\n                this.value = "0 packets";\n            };\n        };\n\n\n        $("save_state").onclick = async function()\n        {\n            const result = await emulator.save_state();\n            dump_file(result, "v86state.bin");\n\n            $("save_state").blur();\n        };\n\n        $("load_state").onclick = function()\n        {\n            $("load_state_input").click();\n            $("load_state").blur();\n        };\n\n        $("load_state_input").onchange = async function()\n        {\n            var file = this.files[0];\n\n            if(!file)\n            {\n                return;\n            }\n\n            var was_running = emulator.is_running();\n\n            if(was_running)\n            {\n                await emulator.stop();\n            }\n\n            var filereader = new FileReader();\n            filereader.onload = async function(e)\n            {\n                try\n                {\n                    await emulator.restore_state(e.target.result);\n                }\n                catch(err)\n                {\n                    alert("Something bad happened while restoring the state:\\n" + err + "\\n\\n" +\n                          "Note that the current configuration must be the same as the original");\n                    throw err;\n                }\n\n                if(was_running)\n                {\n                    emulator.run();\n                }\n            };\n            filereader.readAsArrayBuffer(file);\n\n            this.value = "";\n        };\n\n        $("ctrlaltdel").onclick = function()\n        {\n            emulator.keyboard_send_scancodes([\n                0x1D, // ctrl\n                0x38, // alt\n                0x53, // delete\n\n                // break codes\n                0x1D | 0x80,\n                0x38 | 0x80,\n                0x53 | 0x80,\n            ]);\n\n            $("ctrlaltdel").blur();\n        };\n\n        $("alttab").onclick = function()\n        {\n            emulator.keyboard_send_scancodes([\n                0x38, // alt\n                0x0F, // tab\n            ]);\n\n            setTimeout(function()\n            {\n                emulator.keyboard_send_scancodes([\n                    0x38 | 0x80,\n                    0x0F | 0x80,\n                ]);\n            }, 100);\n\n            $("alttab").blur();\n        };\n\n        $("scale").onchange = function()\n        {\n            var n = parseFloat(this.value);\n\n            if(n || n > 0)\n            {\n                emulator.screen_set_scale(n, n);\n            }\n        };\n\n        $("fullscreen").onclick = function()\n        {\n            emulator.screen_go_fullscreen();\n        };\n\n        $("screen_container").onclick = function()\n        {\n            if(mouse_is_enabled && os_uses_mouse)\n            {\n                emulator.lock_mouse();\n                $("lock_mouse").blur();\n            }\n            else\n            {\n                // allow text selection\n                if(window.getSelection().isCollapsed)\n                {\n                    let phone_keyboard = document.getElementsByClassName("phone_keyboard")[0];\n\n                    // stop mobile browser from scrolling into view when the keyboard is shown\n                    phone_keyboard.style.top = document.body.scrollTop + 100 + "px";\n                    phone_keyboard.style.left = document.body.scrollLeft + 100 + "px";\n\n                    phone_keyboard.focus();\n                }\n            }\n        };\n\n        const phone_keyboard = document.getElementsByClassName("phone_keyboard")[0];\n\n        phone_keyboard.setAttribute("autocorrect", "off");\n        phone_keyboard.setAttribute("autocapitalize", "off");\n        phone_keyboard.setAttribute("spellcheck", "false");\n        phone_keyboard.tabIndex = 0;\n\n        $("screen_container").addEventListener("mousedown", e =>\n        {\n            phone_keyboard.focus();\n        }, false);\n\n        $("take_screenshot").onclick = function()\n        {\n            emulator.screen_make_screenshot();\n            $("take_screenshot").blur();\n        };\n\n        if(emulator.speaker_adapter)\n        {\n            let is_muted = false;\n\n            $("mute").onclick = function()\n            {\n                if(is_muted)\n                {\n                    emulator.speaker_adapter.mixer.set_volume(1, undefined);\n                    is_muted = false;\n                    $("mute").value = "Mute";\n                }\n                else\n                {\n                    emulator.speaker_adapter.mixer.set_volume(0, undefined);\n                    is_muted = true;\n                    $("mute").value = "Unmute";\n                }\n\n                $("mute").blur();\n            };\n        }\n        else\n        {\n            $("mute").remove();\n        }\n\n        window.addEventListener("keydown", ctrl_w_rescue, false);\n        window.addEventListener("keyup", ctrl_w_rescue, false);\n        window.addEventListener("blur", ctrl_w_rescue, false);\n\n        function ctrl_w_rescue(e)\n        {\n            if(e.ctrlKey)\n            {\n                window.onbeforeunload = function()\n                {\n                    window.onbeforeunload = null;\n                    return "CTRL-W cannot be sent to the emulator.";\n                };\n            }\n            else\n            {\n                window.onbeforeunload = null;\n            }\n        }\n    }\n\n    function init_filesystem_panel(emulator)\n    {\n        $("filesystem_panel").style.display = "block";\n\n        $("filesystem_send_file").onchange = function()\n        {\n            Array.prototype.forEach.call(this.files, function(file)\n            {\n                var loader = new v86util.SyncFileBuffer(file);\n                loader.onload = function()\n                {\n                    loader.get_buffer(async function(buffer)\n                    {\n                        await emulator.create_file("/" + file.name, new Uint8Array(buffer));\n                    });\n                };\n                loader.load();\n            }, this);\n\n            this.value = "";\n            this.blur();\n        };\n\n        $("filesystem_get_file").onkeypress = async function(e)\n        {\n            if(e.which !== 13)\n            {\n                return;\n            }\n\n            this.disabled = true;\n\n            let result;\n            try\n            {\n                 result = await emulator.read_file(this.value);\n            }\n            catch(err)\n            {\n                console.log(err);\n            }\n\n            this.disabled = false;\n\n            if(result)\n            {\n                var filename = this.value.replace(/\\/$/, "").split("/");\n                filename = filename[filename.length - 1] || "root";\n\n                dump_file(result, filename);\n                this.value = "";\n            }\n            else\n            {\n                alert("Can\'t read file");\n            }\n        };\n    }\n\n    function debug_start(emulator)\n    {\n        if(!emulator.v86)\n        {\n            return;\n        }\n\n        // called as soon as soon as emulation is started, in debug mode\n        var debug = emulator.v86.cpu.debug;\n\n        $("dump_gdt").onclick = debug.dump_gdt_ldt.bind(debug);\n        $("dump_idt").onclick = debug.dump_idt.bind(debug);\n        $("dump_regs").onclick = debug.dump_regs.bind(debug);\n        $("dump_pt").onclick = debug.dump_page_structures.bind(debug);\n\n        $("dump_log").onclick = function()\n        {\n            dump_file(log_data.join(""), "v86.log");\n        };\n\n        var cpu = emulator.v86.cpu;\n\n        $("debug_panel").style.display = "block";\n        setInterval(function()\n        {\n            $("debug_panel").textContent =\n                cpu.debug.get_regs_short().join("\\n") + "\\n" + cpu.debug.get_state();\n\n            $("dump_log").value = "Dump log" + (log_data.length ? " (" + log_data.length + " lines)" : "");\n        }, 1000);\n\n        // helps debugging\n        window.emulator = emulator;\n        window.cpu = cpu;\n        window.dump_file = dump_file;\n    }\n\n    function onpopstate(e)\n    {\n        location.reload();\n    }\n\n    function set_profile(prof)\n    {\n        if(window.history.pushState)\n        {\n            window.history.pushState({ profile: prof }, "", "?profile=" + prof);\n        }\n    }\n\n})();\n', 'message': 'Replace page_poison=on with init_on_free=on\n', 'author_name': 'Fabian', 'lang': 'js'}