Skip to content

Async CDROM functionality broken when booting from HDD #1349

@ericmackrodt

Description

@ericmackrodt
  • Version of v86: latest-22-g0eec2965
  • Browser/OS: Chrome and Zen (Firefox) on MacOS 15.3.2 (M1 Mac)

Hello,

First I want to congratulate @chschnell, @JoeOsborn and @copy for the incredible work getting the PR that allows booting with both the CD and HDD drives. It's an amazing advancement for the emulator.

The emulator does boot from the HDD with the CD in the drive and the CD image is accessible from within the OS, if the cd is not set to async.

Here's my current config that is doing that, the hard drive image fresh from qemu:

 const initOpts: V86Options = {
      wasm_path: "/v86.wasm",
      memory_size: 64 * 1024 * 1024,
      vga_memory_size: 32 * 1024 * 1024,
      screen_container: emulatorContainer,
      preserve_mac_from_state_image: true,
      hda: { url: "/hdd.img", async: true },
      cdrom: { url: "/98install.iso" }, // cd is not async (also, same image used on qemu to install Windows 98 SE)
      bios: {
        url: "/bios/seabios.bin",
      },
      vga_bios: {
        url: "/bios/vgabios.bin",
      },
      autostart: true,
    };

Keep in mind that, when I boot the machine fresh from qemu, it re-detects the hardware/devices and reboots before I can go to My Computer to check the cd rom state.
But that works perfectly.

Unfortunately I found a couple of issues when it comes to booting from the HDD while having a cd image configured as async.

Scenario 1: Booting the machine with async cdrom image.

If I do the exact same as above, but with the cdrom set to async: true:

 const initOpts: V86Options = {
      wasm_path: "/v86.wasm",
      memory_size: 64 * 1024 * 1024,
      vga_memory_size: 32 * 1024 * 1024,
      screen_container: emulatorContainer,
      preserve_mac_from_state_image: true,
      hda: { url: "/hdd.img", async: true },
      cdrom: { url: "/98install.iso", async: true }, // cd is async
      bios: {
        url: "/bios/seabios.bin",
      },
      vga_bios: {
        url: "/bios/vgabios.bin",
      },
      autostart: true,
    };

The emulator starts and, in the networking tab from the browser's dev tools, I can see the chunks of the ISO are being downloaded, but after the drivers are re-detected and the machine rebooted, I see the label of the cd drive is just Cd and if I try to browse it's files, it's blank:

Image Image

Scenario 2: Save state with ISO and load state with cd being async.

Now, if when I had the cd not as async and, after the machine rebooted and I can browse the files in the CD, I:

  • save the state of the emulator
    • I get a state file that is over 700mb as the CD image is being stored in state.
  • change the CD to async: true in the initial config
  • refresh the page
    • upon loading the page, v86 starts downloading the chunks for the ISO
  • try to load my state

I get the following error:

Failed to restore state: TypeError: $block_cache$jscomp$1_state$jscomp$3$$ is not iterable
    at $AsyncXHRBuffer$$module$src$buffer$$.set_state (libv86-debug.js:726:57)
    at $IDEInterface$$module$src$ide$$.set_state (libv86-debug.js:9254:30)
    at $IDEChannel$$module$src$ide$$.set_state (libv86-debug.js:8538:15)
    at $IDEController$$module$src$ide$$.set_state (libv86-debug.js:8369:36)
    at $CPU$$module$src$cpu$$.set_state (libv86-debug.js:11028:142)
    at $restore_state$$module$src$state$$ (libv86-debug.js:2256:21)
    at $v86$$module$src$main$$.restore_state (libv86-debug.js:6151:10)
    at $V86$$module$src$browser$starter$$.restore_state (libv86-debug.js:5789:12)
    at filereader.onload (App.tsx:161:24)

Scenario 3: Configuration mismatch during state restoration

  • If I start the emulator without the cdrom:
const initOpts: V86Options = {
    wasm_path: "/v86.wasm",
    memory_size: 64 * 1024 * 1024,
    vga_memory_size: 32 * 1024 * 1024,
    screen_container: emulatorContainer,
    preserve_mac_from_state_image: true,
    hda: { url: "/hdd.img", async: true },
    // cdrom: { url: "/98install.iso", async: true },
    bios: {
        url: "/bios/seabios.bin",
    },
    vga_bios: {
        url: "/bios/vgabios.bin",
    },
    autostart: true,
};
  • then I wait for the drivers to be redetected and the machine to reboot
  • save the state
  • reconfigure the cdrom by uncommenting cdrom: { url: "/98install.iso", async: true },
  • reload the state that was saved on v86 without a cd-drive configured

The machine freezes with a black screen and I get the following error:

Failed to restore state: TypeError: Cannot read properties of null (reading '0')
    at $AsyncXHRBuffer$$module$src$buffer$$.set_state (libv86-debug.js:723:82)
    at $IDEInterface$$module$src$ide$$.set_state (libv86-debug.js:9254:30)
    at $IDEChannel$$module$src$ide$$.set_state (libv86-debug.js:8538:15)
    at $IDEController$$module$src$ide$$.set_state (libv86-debug.js:8369:36)
    at $CPU$$module$src$cpu$$.set_state (libv86-debug.js:11028:142)
    at $restore_state$$module$src$state$$ (libv86-debug.js:2256:21)
    at $v86$$module$src$main$$.restore_state (libv86-debug.js:6151:10)
    at $V86$$module$src$browser$starter$$.restore_state (libv86-debug.js:5789:12)
    at filereader.onload (App.tsx:160:24)
filereader.onload @ App.tsx:164
FileReader
handleRestoreState @ App.tsx:175Understand this error

This specific issue happens regardless if the cdrom setting is async: true or async: false before trying to load the saved state.

Scenario 4: Load state saved with different ISO from initial options.

Now, if I go back to the settings and:

  • remove async: true
  • change the cd image to something else:
 const initOpts: V86Options = {
      wasm_path: "/v86.wasm",
      memory_size: 64 * 1024 * 1024,
      vga_memory_size: 32 * 1024 * 1024,
      screen_container: emulatorContainer,
      preserve_mac_from_state_image: true,
      hda: { url: "/hdd.img", async: true },
      cdrom: { url: "/CDROM32.iso" }, // different image
      bios: {
        url: "/bios/seabios.bin",
      },
      vga_bios: {
        url: "/bios/vgabios.bin",
      },
      autostart: true,
    };
  • Refresh the page
    • The machine will start loading and the CDROM32.iso will download
  • Load my state

The CDROM that will be available for browsing will be "98install.iso" from the saved state, not the newly configured CDROM32.iso in initial options.

Scenario 5: Load state containing ISO when initial options have no cdrom option

Now, what's fascinating is, if I comment out the cdrom in the settings:

const initOpts: V86Options = {
      wasm_path: "/v86.wasm",
      memory_size: 64 * 1024 * 1024,
      vga_memory_size: 32 * 1024 * 1024,
      screen_container: emulatorContainer,
      preserve_mac_from_state_image: true,
      hda: { url: "/hdd.img", async: true },
      // cdrom: { url: "/CDROM32.iso", async: true },
      bios: {
        url: "/bios/seabios.bin",
      },
      vga_bios: {
        url: "/bios/vgabios.bin",
      },
      autostart: true,
    };
  • then refresh the page
    • there's no image to be downloaded, so it would be an empty drive
  • load my state
  • the 98install.iso label from the image in the saved state is present in the D: drive momentarily and then turns back to an empty drive.

If I double click d: while the label is still there, I get an error saying: "D:\ Directory name is invalid", which is not the normal error for an empty cd drive.
But if I click ok and then double click again, then I get the normal "D:\ is not accessible" error. (If I had waited until the label disappeared, then I would've gotten this straight away).

So basically, if the state has an image in it, it needs any image (not async) in the initial options so you can navigate the image that is in the state.
If there's no image in the initial options, machine understand that there's no cd in the drive and it goes back to an empty drive.
If there's an image set as async in the initial options and I load my state, v86 crashes.

Scenario 6: State saved without CD-ROM in drive then try to set_cdrom async.

  • If I setup the machine with the hdd image fresh from qemu without the cdrom setting:
 const initOpts: V86Options = {
      wasm_path: "/v86.wasm",
      memory_size: 64 * 1024 * 1024,
      vga_memory_size: 32 * 1024 * 1024,
      screen_container: emulatorContainer,
      preserve_mac_from_state_image: true,
      hda: { url: "/hdd.img", async: true },
     // cdrom: { url: "/98setup.iso", async: true },
      bios: {
        url: "/bios/seabios.bin",
      },
      vga_bios: {
        url: "/bios/vgabios.bin",
      },
      autostart: true,
    };
  • Then, after the drivers are re-detected and the machine reboots, I save the state.
    • I'll get a small, 70(ish) mb, state as there's no ISO.
  • Then I refresh the page
  • Load the state
    • the state loads perfectly
  • Then, similar to earlier, If I try to load my image using emulator.set_cdrom({ url: "/CDROM32.iso", async: true });
    • The chunks of the image will start being download

This sets the label of the cd to Cd and if you try to browse it's files, it shows no files:

Image Image

(these are the same images as before, because it is the same thing)

If I try this without async emulator.set_cdrom({ url: "/CDROM32.iso" });, it does work and the cd image is loaded correctly.

If I save the state now, I get the huge state file like the one I got when I save the state after booting the emulator with a cdrom image non-async, and all of the scenarios from the beginning can be replicated with this state file.

I have tested all of this on Windows 98 SE but I'm sure it can be reproduced one way or another on any operating system.

I would love to be able fix this issue and submit a PR but this work is too low level and goes beyond my skill-set. What I can do is, if deemed useful, I can write some tests for this scenario in the test suite and if someone attempts to fix these issues they can run those tests to verify if they were fixed.

As for my own project, I'm okay with using images in a non-async manner for now.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions