Browse Source

Add V86.set_fda, V86.eject_fda + a test for floppy insertion/ejection

Fabian 8 months ago
parent
commit
eaae139c6f
4 changed files with 141 additions and 63 deletions
  1. 1 0
      Makefile
  2. 42 63
      src/browser/starter.js
  3. 57 0
      src/buffer.js
  4. 41 0
      tests/api/floppy-insert-eject.js

+ 1 - 0
Makefile

@@ -312,6 +312,7 @@ api-tests: all-debug
 	./tests/api/clean-shutdown.js
 	./tests/api/state.js
 	./tests/api/reset.js
+	./tests/api/floppy-insert-eject.js
 
 all-tests: jshint kvm-unit-test qemutests qemutests-release jitpagingtests api-tests nasmtests nasmtests-force-jit tests expect-tests
 	# Skipping:

+ 42 - 63
src/browser/starter.js

@@ -395,78 +395,21 @@ V86Starter.prototype.continue_init = async function(emulator, options)
             file.async = false;
         }
 
-        if(file.buffer instanceof ArrayBuffer)
+        if(file.url && !file.async)
         {
-            var buffer = new v86util.SyncBuffer(file.buffer);
             files_to_load.push({
                 name: name,
-                loadable: buffer,
+                url: file.url,
+                size: file.size,
             });
         }
-        else if(typeof File !== "undefined" && file.buffer instanceof File)
+        else
         {
-            // SyncFileBuffer:
-            // - loads the whole disk image into memory, impossible for large files (more than 1GB)
-            // - can later serve get/set operations fast and synchronously
-            // - takes some time for first load, neglectable for small files (up to 100Mb)
-            //
-            // AsyncFileBuffer:
-            // - loads slices of the file asynchronously as requested
-            // - slower get/set
-
-            // Heuristics: If file is larger than or equal to 256M, use AsyncFileBuffer
-            if(file.async === undefined)
-            {
-                file.async = file.buffer.size >= 256 * 1024 * 1024;
-            }
-
-            if(file.async)
-            {
-                var buffer = new v86util.AsyncFileBuffer(file.buffer);
-            }
-            else
-            {
-                var buffer = new v86util.SyncFileBuffer(file.buffer);
-            }
-
             files_to_load.push({
-                name: name,
-                loadable: buffer,
+                name,
+                loadable: v86util.buffer_from_object(file),
             });
         }
-        else if(file.url)
-        {
-            if(file.async)
-            {
-                let buffer;
-
-                if(file.use_parts)
-                {
-                    buffer = new v86util.AsyncXHRPartfileBuffer(file.url, file.size, file.fixed_chunk_size, false, this.zstd_decompress_worker.bind(this));
-                }
-                else
-                {
-                    buffer = new v86util.AsyncXHRBuffer(file.url, file.size, file.fixed_chunk_size);
-                }
-
-                files_to_load.push({
-                    name: name,
-                    loadable: buffer,
-                });
-            }
-            else
-            {
-                files_to_load.push({
-                    name: name,
-                    url: file.url,
-                    size: file.size,
-                });
-            }
-        }
-        else
-        {
-            dbg_log("Ignored file: url=" + file.url + " buffer=" + file.buffer);
-        }
     };
 
     if(options["state"])
@@ -1023,6 +966,42 @@ V86Starter.prototype.is_running = function()
     return this.cpu_is_running;
 };
 
+/**
+ * Set the image inserted in the floppy drive. Can be changed at runtime, as
+ * when physically changing the floppy disk.
+ * @export
+ */
+V86Starter.prototype.set_fda = async function(file)
+{
+    if(file.url && !file.async)
+    {
+        v86util.load_file(file.url, {
+            done: result =>
+            {
+                this.v86.cpu.devices.fdc.set_fda(new v86util.SyncBuffer(result));
+            },
+        });
+    }
+    else
+    {
+        const image = v86util.buffer_from_object(file);
+        image.onload = () =>
+        {
+            this.v86.cpu.devices.fdc.set_fda(image);
+        };
+        image.load();
+    }
+};
+
+/**
+ * Eject the floppy drive.
+ * @export
+ */
+V86Starter.prototype.eject_fda = function()
+{
+    this.v86.cpu.devices.fdc.eject_fda();
+};
+
 /**
  * Send a sequence of scan codes to the emulated PS2 controller. A list of
  * codes can be found at http://stanislavs.org/helppc/make_codes.html.

+ 57 - 0
src/buffer.js

@@ -8,6 +8,8 @@
     v86util.AsyncFileBuffer = AsyncFileBuffer;
     v86util.SyncFileBuffer = SyncFileBuffer;
 
+    v86util.buffer_from_object = buffer_from_object;
+
     // The smallest size the emulated hardware can emit
     const BLOCK_SIZE = 256;
 
@@ -744,4 +746,59 @@
             });
         };
     }
+
+    function buffer_from_object(obj)
+    {
+        // TODO: accept Uint8Array, ArrayBuffer, File, url rather than { url }
+
+        if(obj.buffer instanceof ArrayBuffer)
+        {
+            return new v86util.SyncBuffer(obj.buffer);
+        }
+        else if(typeof File !== "undefined" && obj.buffer instanceof File)
+        {
+            // SyncFileBuffer:
+            // - loads the whole disk image into memory, impossible for large files (more than 1GB)
+            // - can later serve get/set operations fast and synchronously
+            // - takes some time for first load, neglectable for small files (up to 100Mb)
+            //
+            // AsyncFileBuffer:
+            // - loads slices of the file asynchronously as requested
+            // - slower get/set
+
+            // Heuristics: If file is larger than or equal to 256M, use AsyncFileBuffer
+            let is_async = obj.async;
+            if(is_async === undefined)
+            {
+                is_async = obj.buffer.size >= 256 * 1024 * 1024;
+            }
+
+            if(is_async)
+            {
+                return new v86util.AsyncFileBuffer(obj.buffer);
+            }
+            else
+            {
+                return new v86util.SyncFileBuffer(obj.buffer);
+            }
+        }
+        else if(obj.url)
+        {
+            // Note: Only async for now
+
+            if(obj.use_parts)
+            {
+                const zstd_decompress = null; // TODO
+                return new v86util.AsyncXHRPartfileBuffer(obj.url, obj.size, obj.fixed_chunk_size, false, zstd_decompress);
+            }
+            else
+            {
+                return new v86util.AsyncXHRBuffer(obj.url, obj.size, obj.fixed_chunk_size);
+            }
+        }
+        else
+        {
+            dbg_log("Ignored file: url=" + obj.url + " buffer=" + obj.buffer);
+        }
+    }
 })();

+ 41 - 0
tests/api/floppy-insert-eject.js

@@ -0,0 +1,41 @@
+#!/usr/bin/env node
+"use strict";
+
+const TEST_RELEASE_BUILD = +process.env.TEST_RELEASE_BUILD;
+
+const fs = require("fs");
+var V86 = require(`../../build/${TEST_RELEASE_BUILD ? "libv86" : "libv86-debug"}.js`).V86;
+
+process.on("unhandledRejection", exn => { throw exn; });
+
+const emulator = new V86({
+    bios: { url: __dirname + "/../../bios/seabios.bin" },
+    vga_bios: { url: __dirname + "/../../bios/vgabios.bin" },
+    hda: { url: __dirname + "/../../images/msdos.img" },
+    network_relay_url: "<UNUSED>",
+    autostart: true,
+    memory_size: 32 * 1024 * 1024,
+    filesystem: {},
+    log_level: 0,
+    screen_dummy: true,
+});
+
+emulator.automatically([
+    { sleep: 1 },
+    { vga_text: "C:\\> " },
+    { keyboard_send: "dir A:\n" },
+    { vga_text: "Abort, Retry, Fail?" },
+    { keyboard_send: "F" },
+    { call: () => {
+            emulator.set_fda({ url: __dirname + "/../../images/freedos722.img" });
+        },
+    },
+    { keyboard_send: "dir A:\n" },
+    { sleep: 1 },
+    { vga_text: "FDOS         <DIR>" },
+    { call: () => {
+            console.log("Passed");
+            emulator.stop();
+        }
+    },
+]);