From e3b43c1f9ec82bbc3fe37fbb1d686784c20d34b7 Mon Sep 17 00:00:00 2001 From: chschnell Date: Wed, 4 Sep 2024 14:16:14 +0200 Subject: [PATCH 01/39] Add support for VBE_DISPI_INDEX_X_OFFSET Adds support for horizontal offset in Bochs VESA BIOS Extension. Should fix issue #1088. --- src/vga.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/vga.js b/src/vga.js index 8e746f9365..cf30eadeee 100644 --- a/src/vga.js +++ b/src/vga.js @@ -220,7 +220,7 @@ function VGAScreen(cpu, bus, vga_memory_size) * @type {number} */ this.svga_offset = 0; - + this.svga_offset_x = 0; this.svga_offset_y = 0; const pci_revision = 0; // set to 2 for qemu extended registers @@ -2123,14 +2123,23 @@ VGAScreen.prototype.port1CF_write = function(value) dbg_log("SVGA bank offset: " + h(value << 16), LOG_VGA); this.svga_bank_offset = value << 16; break; + case 8: + // x offset + dbg_log("SVGA X offset: " + h(value), LOG_VGA); + if(this.svga_offset_x !== value) + { + this.svga_offset_x = value; + this.svga_offset = this.svga_offset_y * this.svga_width + this.svga_offset_x; + this.complete_redraw(); + } + break; case 9: // y offset - const offset = value * this.svga_width; - dbg_log("SVGA offset: " + h(offset) + " y=" + h(value), LOG_VGA); + dbg_log("SVGA Y offset: " + h(value * this.svga_width) + " y=" + h(value), LOG_VGA); if(this.svga_offset_y !== value) { this.svga_offset_y = value; - this.svga_offset = offset; + this.svga_offset = this.svga_offset_y * this.svga_width + this.svga_offset_x; this.complete_redraw(); } break; @@ -2204,7 +2213,7 @@ VGAScreen.prototype.svga_register_read = function(n) case 8: // x offset - return 0; + return this.svga_offset_x; case 9: return this.svga_offset_y; case 0x0A: From 283bda1e450a041840de0df94e81f9073f1170bd Mon Sep 17 00:00:00 2001 From: chschnell Date: Sun, 8 Sep 2024 19:02:23 +0200 Subject: [PATCH 02/39] Reset X- and Y-offsets when enabling SVGA video mode Reset members svga_offset, svga_offset_x and svga_offset_y to 0 when enabling any SVGA video mode. Behaviour copied from Bochs at: https://sourceforge.net/p/bochs/code/HEAD/tree/trunk/bochs/iodev/display/vga.cc#l1068 --- src/vga.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vga.js b/src/vga.js index cf30eadeee..0f2f1de272 100644 --- a/src/vga.js +++ b/src/vga.js @@ -2163,6 +2163,9 @@ VGAScreen.prototype.port1CF_write = function(value) if(this.svga_enabled && this.dispi_index === 4) { + this.svga_offset = 0; + this.svga_offset_x = 0; + this.svga_offset_y = 0; this.set_size_graphical(this.svga_width, this.svga_height, this.svga_bpp, this.svga_width, this.svga_height); this.bus.send("screen-set-mode", true); this.graphical_mode = true; From 15c661bf671a28bef5e910e55e0e4e9184612fb7 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 12 Sep 2024 21:44:38 +0200 Subject: [PATCH 03/39] add issue template --- .github/ISSUE_TEMPLATE/issue.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/issue.md diff --git a/.github/ISSUE_TEMPLATE/issue.md b/.github/ISSUE_TEMPLATE/issue.md new file mode 100644 index 0000000000..8e499465ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue.md @@ -0,0 +1,24 @@ +--- +name: Issue +about: Bug reports or feature requests +title: '' +labels: '' +assignees: '' + +--- + + From cb3be277679321744874c7574437de3e7f38d851 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 12 Sep 2024 21:28:54 -0600 Subject: [PATCH 04/39] add empty package.json (fixes #1145) --- package.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 package.json diff --git a/package.json b/package.json new file mode 100644 index 0000000000..f7f7aee365 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "name": "v86", + "version": "0", + "license": "BSD-2-Clause" +} From 50e3c68e9ad5163c8b0553b30f69c1118161a1ca Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 13 Aug 2024 14:35:16 +0200 Subject: [PATCH 05/39] two instances example: show how to configure network --- examples/two_instances.html | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/examples/two_instances.html b/examples/two_instances.html index 392223a6ae..73d236f339 100644 --- a/examples/two_instances.html +++ b/examples/two_instances.html @@ -20,7 +20,7 @@ url: "../bios/vgabios.bin", }, cdrom: { - url: "../images/linux.iso", + url: "../images/linux4.iso", }, autostart: true, }); @@ -35,7 +35,7 @@ url: "../bios/vgabios.bin", }, cdrom: { - url: "../images/linux.iso", + url: "../images/linux4.iso", }, autostart: true, }); @@ -76,7 +76,17 @@ }; -Click on a screen to control it.
+To set up networking: +
+# first VM
+ifconfig eth0 up arp 10.5.0.2
+
+# second VM
+ifconfig eth0 up arp 10.5.0.3
+ping 10.5.0.2
+
+Click on a screen to control it. +
From 831c95f7e52ef82e21323fcaf7c8fac3a061c26c Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 13 Aug 2024 14:38:06 +0200 Subject: [PATCH 06/39] add slitaz --- src/browser/main.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/browser/main.js b/src/browser/main.js index ecdedc85d0..66ab7196ff 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -1023,6 +1023,17 @@ name: "Tinycore", homepage: "http://www.tinycorelinux.net/", }, + { + id: "slitaz", + memory_size: 512 * 1024 * 1024, + hda: { + url: host + "slitaz-rolling-2024.iso", + size: 56573952, + async: false, + }, + name: "SliTaz", + homepage: "https://slitaz.org/", + }, { id: "freenos", memory_size: 256 * 1024 * 1024, From d00b6a4f81de445e3d652cc17a4a444c68de815e Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 13 Aug 2024 14:40:41 +0200 Subject: [PATCH 07/39] add bluejay --- src/browser/main.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/browser/main.js b/src/browser/main.js index 66ab7196ff..5355d17390 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -1146,11 +1146,21 @@ name: "MikeOS", cdrom: { url: host + "mikeos.iso", - size: 11429888, + size: 3311616, async: false, }, homepage: "https://mikeos.sourceforge.net/", }, + { + id: "bluejay", + name: "Blue Jay", + fda: { + url: host + "bj050.img", + size: 1474560, + async: false, + }, + homepage: "https://archiveos.org/blue-jay/", + }, ]; if(DEBUG) From 287c525708f1db7ab89bae2e1d6ba67575001afd Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 13 Aug 2024 15:10:22 +0200 Subject: [PATCH 08/39] add Windows 2 (#1120) --- src/browser/main.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/browser/main.js b/src/browser/main.js index 5355d17390..6674e2f57b 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -417,6 +417,15 @@ }, name: "Windows", }, + { + id: "windows2", + hda: { + url: host + "windows2.img", + size: 4177920, + async: false, + }, + name: "Windows 2.03", + }, { id: "linux26", cdrom: { From 0a00f53a415456748ab7d30bb2e87b5978acf241 Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 13 Aug 2024 15:27:43 +0200 Subject: [PATCH 09/39] (breaking change) remove V86Starter (use V86 instead) --- src/browser/starter.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/browser/starter.js b/src/browser/starter.js index f0ec027d6b..da3e2d149e 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -1408,17 +1408,14 @@ FileNotFoundError.prototype = Error.prototype; // Closure Compiler's way of exporting if(typeof module !== "undefined" && typeof module.exports !== "undefined") { - module.exports["V86Starter"] = V86; module.exports["V86"] = V86; } else if(typeof window !== "undefined") { - window["V86Starter"] = V86; window["V86"] = V86; } else if(typeof importScripts === "function") { // web worker - self["V86Starter"] = V86; self["V86"] = V86; } From d368ba6ba132a6651b3d31ad58b110c3a00c96cc Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 13 Aug 2024 15:30:25 +0200 Subject: [PATCH 10/39] (breaking change) remove exports: CPU, MemoryFileStorage and ServerFileStorageWrapper these are implementation details that shouldn't be part of the public api --- src/browser/fetch_network.js | 1 + src/browser/filestorage.js | 18 ------------------ src/cpu.js | 14 -------------- 3 files changed, 1 insertion(+), 32 deletions(-) diff --git a/src/browser/fetch_network.js b/src/browser/fetch_network.js index c079beb23d..677c7d4b64 100644 --- a/src/browser/fetch_network.js +++ b/src/browser/fetch_network.js @@ -166,5 +166,6 @@ FetchNetworkAdapter.prototype.receive = function(data) if(typeof module !== "undefined" && typeof module.exports !== "undefined") { + // only for testing module.exports["FetchNetworkAdapter"] = FetchNetworkAdapter; } diff --git a/src/browser/filestorage.js b/src/browser/filestorage.js index 701e73ab76..773cb69e75 100644 --- a/src/browser/filestorage.js +++ b/src/browser/filestorage.js @@ -146,21 +146,3 @@ ServerFileStorageWrapper.prototype.uncache = function(sha256sum) { this.storage.uncache(sha256sum); }; - -// Closure Compiler's way of exporting -if(typeof module !== "undefined" && typeof module.exports !== "undefined") -{ - module.exports["MemoryFileStorage"] = MemoryFileStorage; - module.exports["ServerFileStorageWrapper"] = ServerFileStorageWrapper; -} -else if(typeof window !== "undefined") -{ - window["MemoryFileStorage"] = MemoryFileStorage; - window["ServerFileStorageWrapper"] = ServerFileStorageWrapper; -} -else if(typeof importScripts === "function") -{ - // web worker - self["MemoryFileStorage"] = MemoryFileStorage; - self["ServerFileStorageWrapper"] = ServerFileStorageWrapper; -} diff --git a/src/cpu.js b/src/cpu.js index 3360839084..f92aa3b447 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -1674,17 +1674,3 @@ CPU.prototype.device_lower_irq = function(i) this.devices.ioapic.clear_irq(i); } }; - -// Closure Compiler's way of exporting -if(typeof module !== "undefined" && typeof module.exports !== "undefined") -{ - module.exports["CPU"] = CPU; -} -else if(typeof window !== "undefined") -{ - window["CPU"] = CPU; -} -else if(typeof importScripts === "function") -{ - self["CPU"] = CPU; -} From cfd520b7bffc25cbf1caab6e020e2350da7a9bca Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 15 Aug 2024 15:56:59 +0200 Subject: [PATCH 11/39] .cargo/config -> config.toml --- .cargo/{config => config.toml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .cargo/{config => config.toml} (100%) diff --git a/.cargo/config b/.cargo/config.toml similarity index 100% rename from .cargo/config rename to .cargo/config.toml From 981a8bcb78ea6bd9a3e6ce1af0b23b6fc5861d38 Mon Sep 17 00:00:00 2001 From: Fabian Date: Sat, 24 Aug 2024 18:44:52 +0200 Subject: [PATCH 12/39] move bios selector down --- debug.html | 10 +++++----- index.html | 9 ++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/debug.html b/debug.html index 1a65851baf..501bdd868a 100644 --- a/debug.html +++ b/debug.html @@ -136,11 +136,6 @@

Debugger


- - - - - + + + + + diff --git a/index.html b/index.html index 27cd2ab1d1..e3e6622641 100644 --- a/index.html +++ b/index.html @@ -69,11 +69,6 @@

Select profile


Setup


@@ -177,6 +172,11 @@

Debugger



Disk images are not uploaded to the server
- - - - - + + + + From daa80bb608606e647f05574c507583a5ce89cfa4 Mon Sep 17 00:00:00 2001 From: Fabian Date: Sat, 24 Aug 2024 21:15:14 +0200 Subject: [PATCH 13/39] fetch-networking: fix mixed content errors (#1091) --- src/browser/fetch_network.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/browser/fetch_network.js b/src/browser/fetch_network.js index 677c7d4b64..489938942c 100644 --- a/src/browser/fetch_network.js +++ b/src/browser/fetch_network.js @@ -68,10 +68,19 @@ async function on_data_http(data) this.read = ""; let first_line = headers[0].split(" "); - let target = new URL("http://host" + first_line[1]); + let target; if(/^https?:/.test(first_line[1])) { + // HTTP proxy target = new URL(first_line[1]); } + else { + target = new URL("http://host" + first_line[1]); + } + if(typeof window !== "undefined" && target.protocol === "http:" && window.location.protocol === "https:") { + // fix "Mixed Content" errors + target.protocol = "https:"; + } + let req_headers = new Headers(); for(let i = 1; i < headers.length; ++i) { let parts = headers[i].split(": "); From ebccacf0789427c24de1b018de76ef259cedb5a1 Mon Sep 17 00:00:00 2001 From: Fabian Date: Sat, 24 Aug 2024 21:31:03 +0200 Subject: [PATCH 14/39] disable blinking cursor when emulator is paused --- src/browser/screen.js | 10 ++++++++++ v86.css | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/browser/screen.js b/src/browser/screen.js index 67dabee7a4..fb72ec1b13 100644 --- a/src/browser/screen.js +++ b/src/browser/screen.js @@ -284,6 +284,16 @@ function ScreenAdapter(screen_container, bus) stopped = true; }; + // TODO: Should probably not use bus for this + this.bus.register("emulator-stopped", function() + { + cursor_element.classList.remove("blinking-cursor"); + }, this); + this.bus.register("emulator-started", function() + { + cursor_element.classList.add("blinking-cursor"); + }, this); + this.set_mode = function(graphical) { is_graphical = graphical; diff --git a/v86.css b/v86.css index 2926f47c34..d25f480c18 100644 --- a/v86.css +++ b/v86.css @@ -138,10 +138,10 @@ h4 { color: transparent; } } -.cursor { - animation: cursor 0.6s step-start infinite; +.blinking-cursor { + animation: blinking-cursor 0.6s step-start infinite; } -@keyframes cursor { +@keyframes blinking-cursor { 50% { background-color: transparent; } From d8ba239fc00fd9462be6516857f4e1560e5e984b Mon Sep 17 00:00:00 2001 From: Fabian Date: Sat, 24 Aug 2024 21:55:06 +0200 Subject: [PATCH 15/39] document fetch-based networking (#1091) --- docs/networking.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/networking.md b/docs/networking.md index 13b6ebef82..2b6970174a 100644 --- a/docs/networking.md +++ b/docs/networking.md @@ -18,6 +18,17 @@ throttling built-in by default which will degrade the networking. `bellenottelling/websockproxy`docker image has this throttling removed via [websockproxy/issues/4#issuecomment-317255890](https://github.com/benjamincburns/websockproxy/issues/4#issuecomment-317255890). +### fetch-based networking + +v86 supports an experimental networking mode, which is enabled by specifying +`"fetch"` as the relay url. In this mode, no external relay is used and packets +are parsed internally by v86. DHCP and ARP requests are handled by an internal +router, and HTTP requests are translated into calls to `fetch` (which only +works on [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)-enabled +hosts). Additionally, NTP, ICMP pings and UDP echo packets are handled to a +certain degree. See [#1061](https://github.com/copy/v86/pull/1061) for some +technical details. + ### Interaction with state images When using state images, v86 randomises the MAC address after the state has From b877d7f58322fb9cb804d7ada5a6281da14d0a43 Mon Sep 17 00:00:00 2001 From: Fabian Date: Mon, 26 Aug 2024 14:58:11 +0200 Subject: [PATCH 16/39] allow configuring network relay on localhost (just change its default) --- src/browser/main.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/browser/main.js b/src/browser/main.js index 6674e2f57b..c7e416837d 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -1366,6 +1366,12 @@ } } + if(DEBUG && ON_LOCALHOST) + { + // don't use online relay in debug mode + $("networking_proxy").value = "ws://localhost:8080/"; + } + function start_profile(infos) { $("boot_options").style.display = "none"; @@ -1638,7 +1644,7 @@ boot_order: settings.boot_order || parseInt($("boot_order").value, 16) || 0, - network_relay_url: ON_LOCALHOST ? "ws://localhost:8080/" : networking_proxy, + network_relay_url: networking_proxy, bios: settings.bios || bios, vga_bios: settings.bios ? null : vga_bios, From c0c6715a170c3b27dcde4047168ea1b9ba46f519 Mon Sep 17 00:00:00 2001 From: Fabian Date: Mon, 26 Aug 2024 17:30:20 +0200 Subject: [PATCH 17/39] add T3XFORTH suggested by @SuperMaxusa --- src/browser/main.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/browser/main.js b/src/browser/main.js index c7e416837d..f9ec797807 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -1170,6 +1170,16 @@ }, homepage: "https://archiveos.org/blue-jay/", }, + { + id: "t3xforth", + name: "T3XFORTH", + fda: { + url: host + "t3xforth.img", + size: 1474560, + async: false, + }, + homepage: "https://t3x.org/t3xforth/", + }, ]; if(DEBUG) From a430ff0e848dde7c1c28be2d8b2f65ac30d189f0 Mon Sep 17 00:00:00 2001 From: Fabian Date: Mon, 26 Aug 2024 17:35:06 +0200 Subject: [PATCH 18/39] always load floppy disks completely --- src/browser/main.js | 20 -------------------- src/browser/starter.js | 6 ++++++ 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/browser/main.js b/src/browser/main.js index f9ec797807..6f0d3c8386 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -372,7 +372,6 @@ fda: { url: host + "freedos722.img", size: 737280, - async: false, }, name: "FreeDOS", }, @@ -413,7 +412,6 @@ fda: { url: host + "windows101.img", size: 1474560, - async: false, }, name: "Windows", }, @@ -545,7 +543,6 @@ host + "kolibri.img" : "//builds.kolibrios.org/en_US/data/data/kolibri.img", size: 1474560, - async: false, }, name: "KolibriOS", homepage: "https://kolibrios.org/en/", @@ -555,7 +552,6 @@ fda: { url: host + "kolibri.img", size: 1474560, - async: false, }, name: "KolibriOS", }, @@ -639,7 +635,6 @@ id: "solos", fda: { url: host + "os8.img", - async: false, size: 1474560, }, name: "Sol OS", @@ -649,7 +644,6 @@ id: "bootchess", fda: { url: host + "bootchess.img", - async: false, size: 1474560, }, name: "BootChess", @@ -659,7 +653,6 @@ id: "bootbasic", fda: { url: host + "bootbasic.img", - async: false, size: 1474560, }, name: "bootBASIC", @@ -669,7 +662,6 @@ id: "sectorlisp", fda: { url: host + "sectorlisp-friendly.bin", - async: false, size: 512, }, name: "SectorLISP", @@ -679,7 +671,6 @@ id: "sectorforth", fda: { url: host + "sectorforth.img", - async: false, size: 512, }, name: "sectorforth", @@ -689,7 +680,6 @@ id: "floppybird", fda: { url: host + "floppybird.img", - async: false, size: 1474560, }, name: "Floppy Bird", @@ -699,7 +689,6 @@ id: "stillalive", fda: { url: host + "stillalive-os.img", - async: false, size: 368640, }, name: "Still Alive", @@ -709,7 +698,6 @@ id: "hello-v86", fda: { url: host + "hello-v86.img", - async: false, size: 512, }, name: "Hello v86", @@ -936,7 +924,6 @@ fda: { url: host + "snowdrop.img", size: 1440 * 1024, - async: false, }, name: "Snowdrop", homepage: "http://www.sebastianmihai.com/snowdrop/", @@ -955,7 +942,6 @@ fda: { url: host + "qnx-demo-network-4.05.img", size: 1474560, - async: false }, name: "QNX 4.05", }, @@ -993,7 +979,6 @@ fda: { url: host + "mobius-fd-release5.img", size: 1474560, - async: false, }, name: "Mobius", }, @@ -1105,7 +1090,6 @@ fda: { url: host + "PCMOS386-9-user-patched.img", size: 1440 * 1024, - async: false, }, name: "PC-MOS/386", homepage: "https://github.com/roelandjansen/pcmos386v501", @@ -1115,7 +1099,6 @@ fda: { url: host + "jx-demo.img", size: 1440 * 1024, - async: false, }, name: "JX", homepage: "https://www4.cs.fau.de/Projects/JX/index.html", @@ -1125,7 +1108,6 @@ fda: { url: host + "hOp-0.8.img", size: 1440 * 1024, - async: false, }, name: "House", homepage: "https://programatica.cs.pdx.edu/House/", @@ -1166,7 +1148,6 @@ fda: { url: host + "bj050.img", size: 1474560, - async: false, }, homepage: "https://archiveos.org/blue-jay/", }, @@ -1176,7 +1157,6 @@ fda: { url: host + "t3xforth.img", size: 1474560, - async: false, }, homepage: "https://t3x.org/t3xforth/", }, diff --git a/src/browser/starter.js b/src/browser/starter.js index da3e2d149e..eccc6950e4 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -419,6 +419,12 @@ V86.prototype.continue_init = async function(emulator, options) file.async = false; } + if(name === "fda" || name === "fdb") + { + // small, doesn't make sense loading asynchronously + file.async = false; + } + if(file.url && !file.async) { files_to_load.push({ From 3db6365b4ec099307a386c69c7952b0836c9f6bc Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 27 Aug 2024 13:44:05 +0200 Subject: [PATCH 19/39] Use at least 64mb of memory if initrd is used --- src/cpu.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/cpu.js b/src/cpu.js index f92aa3b447..cc7d69c213 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -702,16 +702,17 @@ CPU.prototype.reset_memory = function() this.mem8.fill(0); }; -/** @export */ -CPU.prototype.create_memory = function(size) +CPU.prototype.create_memory = function(size, minimum_size) { - if(size < 1024 * 1024) + if(size < minimum_size) { - size = 1024 * 1024; + size = minimum_size; + dbg_log("Rounding memory size up to " + size, LOG_CPU); } else if((size | 0) < 0) { size = Math.pow(2, 31) - MMAP_BLOCK_SIZE; + dbg_log("Rounding memory size down to " + size, LOG_CPU); } size = ((size - 1) | (MMAP_BLOCK_SIZE - 1)) + 1 | 0; @@ -730,8 +731,10 @@ CPU.prototype.create_memory = function(size) CPU.prototype.init = function(settings, device_bus) { - this.create_memory(typeof settings.memory_size === "number" ? - settings.memory_size : 1024 * 1024 * 64); + this.create_memory( + settings.memory_size || 64 * 1024 * 1024, + settings.initrd ? 64 * 1024 * 1024 : 1024 * 1024, + ); if(settings.disable_jit) { From 56498d6ce2e2744b7a4e41355564c6a9dae93eb4 Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 27 Aug 2024 21:59:26 +0200 Subject: [PATCH 20/39] add wisp_network.js to debug.html --- debug.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug.html b/debug.html index 501bdd868a..742096260e 100644 --- a/debug.html +++ b/debug.html @@ -13,7 +13,7 @@ "memory.js dma.js pit.js vga.js ps2.js rtc.js uart.js acpi.js apic.js ioapic.js sb16.js " + "ne2k.js state.js virtio.js virtio_console.js bus.js elf.js kernel.js"; -var BROWSER_FILES = "main.js screen.js keyboard.js mouse.js speaker.js serial.js network.js fake_network.js fetch_network.js starter.js worker_bus.js print_stats.js filestorage.js"; +var BROWSER_FILES = "main.js screen.js keyboard.js mouse.js speaker.js serial.js network.js fake_network.js fetch_network.js wisp_network.js starter.js worker_bus.js print_stats.js filestorage.js"; var LIB_FILES = ""; // jor1k stuff From fcdd110cb7d1e4301463e9742a68a6ddac5f5c91 Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 29 Aug 2024 13:52:33 +0200 Subject: [PATCH 21/39] Clean up some tests --- lib/filesystem.js | 4 +- src/browser/starter.js | 47 +++---- tests/benchmark/linux-boot.js | 2 +- tests/devices/fetch_network.js | 229 +++++++------------------------- tests/devices/virtio_9p.js | 153 +++++----------------- tests/devices/wisp_network.js | 232 +++++++-------------------------- tests/jit-paging/run.js | 2 +- tests/qemu/run.js | 1 - 8 files changed, 153 insertions(+), 517 deletions(-) diff --git a/lib/filesystem.js b/lib/filesystem.js index 38b32f4b42..46856e0116 100644 --- a/lib/filesystem.js +++ b/lib/filesystem.js @@ -162,7 +162,7 @@ FS.prototype.HandleEvent = function(id) { this.events = newevents; }; -FS.prototype.load_from_json = function(fs, done) +FS.prototype.load_from_json = function(fs) { dbg_assert(fs, "Invalid fs passed to load_from_json"); @@ -183,8 +183,6 @@ FS.prototype.load_from_json = function(fs, done) //{ // this.Check(); //} - - done && done(); }; FS.prototype.LoadRecursive = function(data, parentid) diff --git a/src/browser/starter.js b/src/browser/starter.js index eccc6950e4..1925e23c96 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -1179,10 +1179,9 @@ V86.prototype.serial_set_clear_to_send = function(serial, status) * @param {string} path Path for the mount point * @param {string|undefined} baseurl * @param {string|undefined} basefs As a JSON string - * @param {function(Object)=} callback * @export */ -V86.prototype.mount_fs = async function(path, baseurl, basefs, callback) +V86.prototype.mount_fs = async function(path, baseurl, basefs) { let file_storage = new MemoryFileStorage(); @@ -1191,39 +1190,26 @@ V86.prototype.mount_fs = async function(path, baseurl, basefs, callback) file_storage = new ServerFileStorageWrapper(file_storage, baseurl); } const newfs = new FS(file_storage, this.fs9p.qidcounter); - const mount = () => - { - const idx = this.fs9p.Mount(path, newfs); - if(!callback) - { - return; - } - if(idx === -ENOENT) - { - callback(new FileNotFoundError()); - } - else if(idx === -EEXIST) - { - callback(new FileExistsError()); - } - else if(idx < 0) - { - dbg_assert(false, "Unexpected error code: " + (-idx)); - callback(new Error("Failed to mount. Error number: " + (-idx))); - } - else - { - callback(null); - } - }; if(baseurl) { dbg_assert(typeof basefs === "object", "Filesystem: basefs must be a JSON object"); - newfs.load_from_json(basefs, () => mount()); + newfs.load_from_json(basefs); } - else + + const idx = this.fs9p.Mount(path, newfs); + + if(idx === -ENOENT) { - mount(); + throw new FileNotFoundError(); + } + else if(idx === -EEXIST) + { + throw new FileExistsError(); + } + else if(idx < 0) + { + dbg_assert(false, "Unexpected error code: " + (-idx)); + throw new Error("Failed to mount. Error number: " + (-idx)); } }; @@ -1354,7 +1340,6 @@ V86.prototype.automatically = function(steps) }; run(steps); - }; /** diff --git a/tests/benchmark/linux-boot.js b/tests/benchmark/linux-boot.js index 8586afb6ad..0fb7c1410c 100755 --- a/tests/benchmark/linux-boot.js +++ b/tests/benchmark/linux-boot.js @@ -48,7 +48,7 @@ else emulator.bus.register("emulator-started", function() { - console.error("Booting now, please stand by"); + console.log("Booting now, please stand by"); start_time = Date.now(); }); diff --git a/tests/devices/fetch_network.js b/tests/devices/fetch_network.js index 4f7d8a49c3..c3f7cf8575 100755 --- a/tests/devices/fetch_network.js +++ b/tests/devices/fetch_network.js @@ -9,28 +9,11 @@ const V86 = require(`../../build/${TEST_RELEASE_BUILD ? "libv86" : "libv86-debug const assert = require("assert").strict; const SHOW_LOGS = false; -const STOP_ON_FIRST_FAILURE = false; - -function log_pass(msg, ...args) -{ - console.log(`\x1b[92m[+] ${msg}\x1b[0m`, ...args); -} - -function log_warn(msg, ...args) -{ - console.error(`\x1b[93m[!] ${msg}\x1b[0m`, ...args); -} - -function log_fail(msg, ...args) -{ - console.error(`\x1b[91m[-] ${msg}\x1b[0m`, ...args); -} const tests = [ { name: "DHCP", - timeout: 60, start: () => { emulator.serial0_send("udhcpc\n"); @@ -44,7 +27,6 @@ const tests = }, { name: "ifconfig", - timeout: 60, start: () => { emulator.serial0_send("ifconfig\n"); @@ -58,7 +40,6 @@ const tests = }, { name: "route", - timeout: 60, start: () => { emulator.serial0_send("ip route\n"); @@ -72,7 +53,6 @@ const tests = }, { name: "ping 1.2.3.4", - timeout: 60, start: () => { emulator.serial0_send("ping -c 2 1.2.3.4\n"); @@ -86,7 +66,6 @@ const tests = }, { name: "arp -a", - timeout: 60, start: () => { emulator.serial0_send("arp -a\n"); @@ -100,7 +79,6 @@ const tests = }, { name: "Curl mocked.example.org", - timeout: 60, allow_failure: true, start: () => { @@ -115,7 +93,6 @@ const tests = }, { name: "Curl example.org", - timeout: 60, allow_failure: true, start: () => { @@ -131,10 +108,6 @@ const tests = ]; -let test_num = 0; -let test_timeout = 0; -const failed_tests = []; - const emulator = new V86({ bios: { url: __dirname + "/../../bios/seabios.bin" }, vga_bios: { url: __dirname + "/../../bios/vgabios.bin" }, @@ -162,191 +135,89 @@ emulator.add_listener("emulator-ready", function () { }; }); -let ran_command = false; +let test_num = 0; +let booted = false; let line = ""; -let capturing = false; let capture = ""; -let next_trigger; -let next_trigger_handler; - -function start_timeout() -{ - if(tests[test_num].timeout) - { - test_timeout = setTimeout(() => - { - log_fail("Test #%d (%s) took longer than %s sec. Timing out and terminating.", test_num, tests[test_num].name, tests[test_num].timeout); - process.exit(1); - }, tests[test_num].timeout * 1000); - } -} +let end_trigger; -function begin() +emulator.bus.register("emulator-started", function() { - start_timeout(); - - console.log("\nPreparing test #%d: %s", test_num, tests[test_num].name); - start_test(); -} + console.log("Booting now, please stand by"); +}); -function start_test() +emulator.add_listener("serial0-output-byte", function(byte) { - console.log("Starting test #%d: %s", test_num, tests[test_num].name); - - capture = ""; - - tests[test_num].start(); + const chr = String.fromCharCode(byte); + if(chr < " " && chr !== "\n" && chr !== "\t" || chr > "~") + { + return; + } - if(tests[test_num].capture_trigger) + let new_line = ""; + if(chr === "\n") { - next_trigger = tests[test_num].capture_trigger; - next_trigger_handler = start_capture; + console.log(" Captured: %s", line); + new_line = line; + capture += line + "\n"; + line = ""; } else { - next_trigger = tests[test_num].end_trigger; - next_trigger_handler = end_test; + line += chr; } - start_capture(); -} -function start_capture() -{ - console.log("Capturing..."); - capture = ""; - capturing = true; - - next_trigger = tests[test_num].end_trigger; - next_trigger_handler = end_test; -} - -function end_test() -{ - capturing = false; - - if(tests[test_num].timeout) + if(new_line === end_trigger) { - clearTimeout(test_timeout); - } + let test_has_failed = false; - let test_has_failed = false; - - try { - tests[test_num].end(capture); - } catch(e) { - console.log(e); - test_has_failed = true; - } + try { + tests[test_num].end(capture); + } catch(e) { + console.log(e); + test_has_failed = true; + } - if(!test_has_failed) - { - log_pass("Test #%d passed: %s", test_num, tests[test_num].name); - } - else - { - if(tests[test_num].allow_failure) + if(!test_has_failed) { - log_warn("Test #%d failed: %s (failure allowed)", test_num, tests[test_num].name); + console.log("[+] Test #%d passed: %s", test_num, tests[test_num].name); } else { - log_fail("Test #%d failed: %s", test_num, tests[test_num].name); - - if(STOP_ON_FIRST_FAILURE) + if(tests[test_num].allow_failure) + { + console.warn("[!] Test #%d failed: %s (failure allowed)", test_num, tests[test_num].name); + } + else { - finish_tests(); + console.error("[-] Test #%d failed: %s", test_num, tests[test_num].name); + process.exit(1); } } - test_has_failed = false; - } - test_num++; + test_num++; - if(test_num < tests.length) - { - begin(); - } - else - { - finish_tests(); } -} - -function finish_tests() -{ - emulator.stop(); - console.log("\nTests finished."); - if(failed_tests.length === 0) + if(!booted && line.endsWith("~% ") || new_line === end_trigger) { - console.log("All tests passed"); - } - else - { - let unallowed_failure = false; + booted = true; - console.error("Failed %d out of %d tests:", failed_tests.length, tests.length); - for(const num of failed_tests) + if(test_num >= tests.length) { - if(tests[num].allow_failure) - { - log_warn("#%d %s (failure allowed)", num, tests[num].name); - } - else - { - unallowed_failure = true; - log_fail("#%d %s", num, tests[num].name); - } + emulator.stop(); + emulator.destroy(); + + console.log("Tests finished."); } - if(unallowed_failure) + else { - process.exit(1); - } - } -} - -emulator.bus.register("emulator-started", function() -{ - console.error("Booting now, please stand by"); -}); - -emulator.add_listener("serial0-output-byte", function(byte) -{ - const chr = String.fromCharCode(byte); - if(chr < " " && chr !== "\n" && chr !== "\t" || chr > "~") - { - return; - } + console.log("Starting test #%d: %s", test_num, tests[test_num].name); - let new_line = ""; - let is_new_line = false; - if(chr === "\n") - { - is_new_line = true; - new_line = line; - line = ""; - } - else - { - line += chr; - } + capture = ""; + end_trigger = tests[test_num].end_trigger; - if(!ran_command && line.endsWith("~% ")) - { - ran_command = true; - begin(); - } - else if(new_line === next_trigger) - { - next_trigger_handler(); - } - else if(is_new_line && capturing) - { - capture += new_line + "\n"; - console.log(" Captured: %s", new_line); - } - else if(is_new_line) - { - console.log(" Serial: %s", new_line); + tests[test_num].start(); + } } }); diff --git a/tests/devices/virtio_9p.js b/tests/devices/virtio_9p.js index 44a46115f5..88bad53b90 100755 --- a/tests/devices/virtio_9p.js +++ b/tests/devices/virtio_9p.js @@ -12,28 +12,13 @@ const testfsjson = require("./testfs.json"); const SHOW_LOGS = false; const STOP_ON_FIRST_FAILURE = false; -function log_pass(msg, ...args) -{ - console.log(`\x1b[92m[+] ${msg}\x1b[0m`, ...args); -} - -function log_warn(msg, ...args) -{ - console.error(`\x1b[93m[!] ${msg}\x1b[0m`, ...args); -} - -function log_fail(msg, ...args) -{ - console.error(`\x1b[91m[-] ${msg}\x1b[0m`, ...args); -} - function assert_equal(actual, expected, message) { if(actual !== expected) { - log_warn("Failed assert equal (Test: %s). %s", tests[test_num].name, message || ""); - log_warn("Expected:\n" + expected); - log_warn("Actual:\n" + actual); + console.warn("Failed assert equal (Test: %s). %s", tests[test_num].name, message || ""); + console.warn("Expected:\n" + expected); + console.warn("Actual:\n" + actual); test_fail(); } } @@ -42,8 +27,8 @@ function assert_not_equal(actual, expected, message) { if(actual === expected) { - log_warn("Failed assert not equal (Test: %s). %s", tests[test_num].name, message || ""); - log_warn("Expected something different than:\n" + expected); + console.warn("Failed assert not equal (Test: %s). %s", tests[test_num].name, message || ""); + console.warn("Expected something different than:\n" + expected); test_fail(); } } @@ -164,7 +149,7 @@ const tests = assert_equal(data.length, 512 * 1024); if(data.find(v => v !== 0)) { - log_warn("Fail: Incorrect data. Expected all zeros."); + console.warn("Fail: Incorrect data. Expected all zeros."); test_fail(); } done(); @@ -663,7 +648,7 @@ const tests = const outputs = capture.split("\n").map(output => output.split(/\s+/)); if(outputs.length < 3) { - log_warn("Wrong format: %s", capture); + console.warn("Wrong format: %s", capture); test_fail(); done(); return; @@ -732,7 +717,7 @@ const tests = if(outputs.length < 3) { - log_warn("Wrong format (expected 3 rows): %s", capture); + console.warn("Wrong format (expected 3 rows): %s", capture); test_fail(); done(); return; @@ -1567,96 +1552,46 @@ let capture = ""; let next_trigger; let next_trigger_handler; -function start_timeout() +async function prepare_test() { + console.log("\nPreparing test #%d: %s", test_num, tests[test_num].name); + if(tests[test_num].timeout) { test_timeout = setTimeout(() => { - log_fail("Test #%d (%s) took longer than %s sec. Timing out and terminating.", test_num, tests[test_num].name, tests[test_num].timeout); + console.error("[-] Test #%d (%s) took longer than %s sec. Timing out and terminating.", test_num, tests[test_num].name, tests[test_num].timeout); process.exit(1); }, tests[test_num].timeout * 1000); } -} - -function nuke_fs() -{ - start_timeout(); - console.log("\nPreparing test #%d: %s", test_num, tests[test_num].name); console.log(" Nuking /mnt"); - emulator.fs9p.RecursiveDelete(""); - reload_fsjson(); -} -function reload_fsjson() -{ if(tests[test_num].use_fsjson) { console.log(" Reloading files from json"); - emulator.fs9p.load_from_json(testfsjson, () => do_mounts()); - } - else - { - do_mounts(); + emulator.fs9p.load_from_json(testfsjson); } -} -function do_mounts() -{ console.log(" Configuring mounts"); if(tests[test_num].mounts && tests[test_num].mounts.length > 0) { - premount(0); - - function premount(mount_num) - { - const path = tests[test_num].mounts[mount_num].path; - emulator.serial0_send("mkdir -p /mnt" + path + "\n"); - emulator.serial0_send("rmdir /mnt" + path + "\n"); - emulator.serial0_send("echo done-premount\n"); - next_trigger = "done-premount"; - next_trigger_handler = () => mount(mount_num); - } - - function mount(mount_num) - { - const { path, baseurl, basefs } = tests[test_num].mounts[mount_num]; - emulator.mount_fs(path, baseurl, basefs, err => - { - if(err) - { - log_warn("Failed to mount fs required for test %s: %s", - tests[test_num].name, err); - test_fail(); - } - if(mount_num + 1 < tests[test_num].mounts.length) - { - premount(mount_num + 1); - } - else - { - if(test_has_failed) - { - report_test(); - } - else - { - load_files(); - } - } - }); + for(const { path, baseurl, basefs } of tests[test_num].mounts) + { + await async function() { + return new Promise((resolve, reject) => { + emulator.serial0_send("mkdir -p /mnt" + path + "\n"); + emulator.serial0_send("rmdir /mnt" + path + "\n"); + emulator.serial0_send("echo done-premount\n"); + next_trigger = "done-premount"; + next_trigger_handler = resolve; + }); + }(); + emulator.mount_fs(path, baseurl, basefs); } } - else - { - load_files(); - } -} -async function load_files() -{ console.log(" Loading additional files"); if(tests[test_num].files) { @@ -1664,29 +1599,9 @@ async function load_files() for(const f of tests[test_num].files) { await emulator.create_file(f.file, f.data); - - remaining--; - if(!remaining) - { - if(test_has_failed) - { - report_test(); - } - else - { - start_test(); - } - } } } - else - { - start_test(); - } -} -function start_test() -{ console.log("Starting test #%d: %s", test_num, tests[test_num].name); capture = ""; @@ -1731,17 +1646,17 @@ function report_test() { if(!test_has_failed) { - log_pass("Test #%d passed: %s", test_num, tests[test_num].name); + console.log("[+] Test #%d passed: %s", test_num, tests[test_num].name); } else { if(tests[test_num].allow_failure) { - log_warn("Test #%d failed: %s (failure allowed)", test_num, tests[test_num].name); + console.warn("Test #%d failed: %s (failure allowed)", test_num, tests[test_num].name); } else { - log_fail("Test #%d failed: %s", test_num, tests[test_num].name); + console.error("[-] Test #%d failed: %s", test_num, tests[test_num].name); if(STOP_ON_FIRST_FAILURE) { @@ -1755,7 +1670,7 @@ function report_test() if(test_num < tests.length) { - nuke_fs(); + prepare_test(); } else { @@ -1776,17 +1691,17 @@ function finish_tests() { let unallowed_failure = false; - console.error("Failed %d out of %d tests:", failed_tests.length, tests.length); + console.error("[-] Failed %d out of %d tests:", failed_tests.length, tests.length); for(const num of failed_tests) { if(tests[num].allow_failure) { - log_warn("#%d %s (failure allowed)", num, tests[num].name); + console.warn("#%d %s (failure allowed)", num, tests[num].name); } else { unallowed_failure = true; - log_fail("#%d %s", num, tests[num].name); + console.error("[-] #%d %s", num, tests[num].name); } } if(unallowed_failure) @@ -1798,7 +1713,7 @@ function finish_tests() emulator.bus.register("emulator-started", function() { - console.error("Booting now, please stand by"); + console.log("Booting now, please stand by"); }); emulator.add_listener("serial0-output-byte", function(byte) @@ -1825,7 +1740,7 @@ emulator.add_listener("serial0-output-byte", function(byte) if(!ran_command && line.endsWith("~% ")) { ran_command = true; - nuke_fs(); + prepare_test(); } else if(new_line === next_trigger) { diff --git a/tests/devices/wisp_network.js b/tests/devices/wisp_network.js index e0f4d90a23..b20af10fda 100755 --- a/tests/devices/wisp_network.js +++ b/tests/devices/wisp_network.js @@ -9,28 +9,11 @@ const V86 = require(`../../build/${TEST_RELEASE_BUILD ? "libv86" : "libv86-debug const assert = require("assert").strict; const SHOW_LOGS = false; -const STOP_ON_FIRST_FAILURE = false; - -function log_pass(msg, ...args) -{ - console.log(`\x1b[92m[+] ${msg}\x1b[0m`, ...args); -} - -function log_warn(msg, ...args) -{ - console.error(`\x1b[93m[!] ${msg}\x1b[0m`, ...args); -} - -function log_fail(msg, ...args) -{ - console.error(`\x1b[91m[-] ${msg}\x1b[0m`, ...args); -} const tests = [ { name: "DHCP", - timeout: 60, start: () => { emulator.serial0_send("udhcpc\n"); @@ -44,7 +27,6 @@ const tests = }, { name: "ifconfig", - timeout: 60, start: () => { emulator.serial0_send("ifconfig\n"); @@ -58,7 +40,6 @@ const tests = }, { name: "route", - timeout: 60, start: () => { emulator.serial0_send("ip route\n"); @@ -72,7 +53,6 @@ const tests = }, //{ // name: "arp -a", - // timeout: 60, // start: () => // { // emulator.serial0_send("arp -a\n"); @@ -86,7 +66,6 @@ const tests = //}, { name: "Curl example.org", - timeout: 60, allow_failure: true, start: () => { @@ -102,10 +81,6 @@ const tests = ]; -let test_num = 0; -let test_timeout = 0; -const failed_tests = []; - const emulator = new V86({ bios: { url: __dirname + "/../../bios/seabios.bin" }, vga_bios: { url: __dirname + "/../../bios/vgabios.bin" }, @@ -117,196 +92,89 @@ const emulator = new V86({ log_level: SHOW_LOGS ? 0x400000 : 0, }); -emulator.add_listener("emulator-ready", function () { - -}); - -let ran_command = false; +let test_num = 0; +let booted = false; let line = ""; -let capturing = false; let capture = ""; -let next_trigger; -let next_trigger_handler; +let end_trigger; -function start_timeout() -{ - if(tests[test_num].timeout) - { - test_timeout = setTimeout(() => - { - log_fail("Test #%d (%s) took longer than %s sec. Timing out and terminating.", test_num, tests[test_num].name, tests[test_num].timeout); - process.exit(1); - }, tests[test_num].timeout * 1000); - } -} - -function begin() +emulator.bus.register("emulator-started", function() { - start_timeout(); - - console.log("\nPreparing test #%d: %s", test_num, tests[test_num].name); - start_test(); -} + console.log("Booting now, please stand by"); +}); -function start_test() +emulator.add_listener("serial0-output-byte", function(byte) { - console.log("Starting test #%d: %s", test_num, tests[test_num].name); - - capture = ""; - - tests[test_num].start(); + const chr = String.fromCharCode(byte); + if(chr < " " && chr !== "\n" && chr !== "\t" || chr > "~") + { + return; + } - if(tests[test_num].capture_trigger) + let new_line = ""; + if(chr === "\n") { - next_trigger = tests[test_num].capture_trigger; - next_trigger_handler = start_capture; + console.log(" Captured: %s", line); + new_line = line; + capture += line + "\n"; + line = ""; } else { - next_trigger = tests[test_num].end_trigger; - next_trigger_handler = end_test; + line += chr; } - start_capture(); -} - -function start_capture() -{ - console.log("Capturing..."); - capture = ""; - capturing = true; - - next_trigger = tests[test_num].end_trigger; - next_trigger_handler = end_test; -} - -function end_test() -{ - capturing = false; - if(tests[test_num].timeout) + if(new_line === end_trigger) { - clearTimeout(test_timeout); - } - - let test_has_failed = false; + let test_has_failed = false; - try { - tests[test_num].end(capture); - } catch(e) { - console.log(e); - test_has_failed = true; - } + try { + tests[test_num].end(capture); + } catch(e) { + console.log(e); + test_has_failed = true; + } - if(!test_has_failed) - { - log_pass("Test #%d passed: %s", test_num, tests[test_num].name); - } - else - { - if(tests[test_num].allow_failure) + if(!test_has_failed) { - log_warn("Test #%d failed: %s (failure allowed)", test_num, tests[test_num].name); + console.log("[+] Test #%d passed: %s", test_num, tests[test_num].name); } else { - log_fail("Test #%d failed: %s", test_num, tests[test_num].name); - - if(STOP_ON_FIRST_FAILURE) + if(tests[test_num].allow_failure) + { + console.warn("[!] Test #%d failed: %s (failure allowed)", test_num, tests[test_num].name); + } + else { - finish_tests(); + console.error("[-] Test #%d failed: %s", test_num, tests[test_num].name); + process.exit(1); } } - test_has_failed = false; - } - test_num++; + test_num++; - if(test_num < tests.length) - { - begin(); } - else - { - finish_tests(); - } -} - -function finish_tests() -{ - emulator.stop(); - emulator.destroy(); - console.log("\nTests finished."); - if(failed_tests.length === 0) + if(!booted && line.endsWith("~% ") || new_line === end_trigger) { - console.log("All tests passed"); - } - else - { - let unallowed_failure = false; + booted = true; - console.error("Failed %d out of %d tests:", failed_tests.length, tests.length); - for(const num of failed_tests) + if(test_num >= tests.length) { - if(tests[num].allow_failure) - { - log_warn("#%d %s (failure allowed)", num, tests[num].name); - } - else - { - unallowed_failure = true; - log_fail("#%d %s", num, tests[num].name); - } + emulator.stop(); + emulator.destroy(); + + console.log("Tests finished."); } - if(unallowed_failure) + else { - process.exit(1); - } - } -} + console.log("Starting test #%d: %s", test_num, tests[test_num].name); -emulator.bus.register("emulator-started", function() -{ - console.error("Booting now, please stand by"); -}); + capture = ""; + end_trigger = tests[test_num].end_trigger; -emulator.add_listener("serial0-output-byte", function(byte) -{ - const chr = String.fromCharCode(byte); - if(chr < " " && chr !== "\n" && chr !== "\t" || chr > "~") - { - return; - } - - let new_line = ""; - let is_new_line = false; - if(chr === "\n") - { - is_new_line = true; - new_line = line; - line = ""; - } - else - { - line += chr; - } - - if(!ran_command && line.endsWith("~% ")) - { - ran_command = true; - begin(); - } - else if(new_line === next_trigger) - { - next_trigger_handler(); - } - else if(is_new_line && capturing) - { - capture += new_line + "\n"; - console.log(" Captured: %s", new_line); - } - else if(is_new_line) - { - console.log(" Serial: %s", new_line); + tests[test_num].start(); + } } }); diff --git a/tests/jit-paging/run.js b/tests/jit-paging/run.js index 1e32b0d602..735e09d5e5 100755 --- a/tests/jit-paging/run.js +++ b/tests/jit-paging/run.js @@ -23,7 +23,7 @@ var emulator = new V86({ emulator.bus.register("emulator-started", function() { - console.error("Booting now, please stand by"); + console.log("Booting now, please stand by"); emulator.create_file("test-jit", test_executable); }); diff --git a/tests/qemu/run.js b/tests/qemu/run.js index eb603531cd..51c593fc03 100755 --- a/tests/qemu/run.js +++ b/tests/qemu/run.js @@ -23,7 +23,6 @@ var emulator = new V86({ emulator.bus.register("emulator-started", function() { - console.error("Booting now, please stand by"); emulator.create_file("test-i386", test_executable); }); From 994dfa7d97e86613ad2b856c7d6e2c1c76523a6e Mon Sep 17 00:00:00 2001 From: Fabian Date: Thu, 29 Aug 2024 13:57:35 +0200 Subject: [PATCH 22/39] add some docs for wisp networking --- docs/networking.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/networking.md b/docs/networking.md index 2b6970174a..8a1a51585b 100644 --- a/docs/networking.md +++ b/docs/networking.md @@ -29,6 +29,13 @@ hosts). Additionally, NTP, ICMP pings and UDP echo packets are handled to a certain degree. See [#1061](https://github.com/copy/v86/pull/1061) for some technical details. +### wisp networking + +v86 also supports the [wisp +protocol](https://github.com/MercuryWorkshop/wisp-protocol) as a networking +proxy. Wisp servers can be specified with the `wisp://` or `wisps://` prefix. +See #1097 for some information. + ### Interaction with state images When using state images, v86 randomises the MAC address after the state has From 725a4e3b47410b7ce2b09be05522205c74ad1f12 Mon Sep 17 00:00:00 2001 From: Fabian Date: Sun, 1 Sep 2024 14:08:21 +0200 Subject: [PATCH 23/39] update buildroot image with 6.8 kernel, virtio drivers (virtio-net, virtio-balloon, virtio-console) and some networking utils (socat and tcpdump) Thanks @SuperMaxusa --- .github/workflows/ci.yml | 2 +- Makefile | 4 ++-- Readme.md | 2 +- examples/lua.html | 2 +- examples/serial.html | 3 +-- src/browser/main.js | 11 +++++++++++ tests/api/state.js | 8 ++++---- tests/full/run.js | 2 +- 8 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf32d1f5b1..c2ea9e7988 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,7 +90,7 @@ jobs: - name: Download uncached images if: steps.cache-images.outputs.cache-hit != 'true' - run: wget -nv -P images/ https://i.copy.sh/{linux.iso,linux3.iso,linux4.iso,buildroot-bzimage.bin,TinyCore-11.0.iso,oberon.img,msdos.img,openbsd-floppy.img,kolibri.img,windows101.img,os8.img,freedos722.img,mobius-fd-release5.img} + run: wget -nv -P images/ https://i.copy.sh/{linux.iso,linux3.iso,linux4.iso,buildroot-bzimage68.bin,TinyCore-11.0.iso,oberon.img,msdos.img,openbsd-floppy.img,kolibri.img,windows101.img,os8.img,freedos722.img,mobius-fd-release5.img} - name: Run api-tests run: make api-tests diff --git a/Makefile b/Makefile index 4a31a91c6b..f079198c8d 100644 --- a/Makefile +++ b/Makefile @@ -249,9 +249,9 @@ $(CLOSURE): # don't upgrade until https://github.com/google/closure-compiler/issues/3972 is fixed wget -nv -O $(CLOSURE) https://repo1.maven.org/maven2/com/google/javascript/closure-compiler/v20210601/closure-compiler-v20210601.jar -build/integration-test-fs/fs.json: +build/integration-test-fs/fs.json: images/buildroot-bzimage68.bin mkdir -p build/integration-test-fs/flat - cp images/buildroot-bzimage.bin build/integration-test-fs/bzImage + cp images/buildroot-bzimage68.bin build/integration-test-fs/bzImage touch build/integration-test-fs/initrd cd build/integration-test-fs && tar cfv fs.tar bzImage initrd ./tools/fs2json.py build/integration-test-fs/fs.tar --out build/integration-test-fs/fs.json diff --git a/Readme.md b/Readme.md index c668e5470c..7859752468 100644 --- a/Readme.md +++ b/Readme.md @@ -152,7 +152,7 @@ for a full setup on Debian or The disk images for testing are not included in this repository. You can download them directly from the website using: -`wget -P images/ https://i.copy.sh/{linux3.iso,linux.iso,linux4.iso,buildroot-bzimage.bin,openbsd-floppy.img,kolibri.img,windows101.img,os8.img,freedos722.img}` +`wget -P images/ https://i.copy.sh/{linux3.iso,linux.iso,linux4.iso,buildroot-bzimage68.bin,openbsd-floppy.img,kolibri.img,windows101.img,os8.img,freedos722.img}` Run integration tests: `make tests` diff --git a/examples/lua.html b/examples/lua.html index efb3ce74a7..5346b3a6d4 100644 --- a/examples/lua.html +++ b/examples/lua.html @@ -22,7 +22,7 @@ url: "../bios/vgabios.bin", }, bzimage: { - url: "../images/buildroot-bzimage.bin", + url: "../images/buildroot-bzimage68.bin", }, autostart: true, disable_keyboard: true, diff --git a/examples/serial.html b/examples/serial.html index ab50c4d4be..8f91975463 100644 --- a/examples/serial.html +++ b/examples/serial.html @@ -20,8 +20,7 @@ url: "../bios/vgabios.bin", }, bzimage: { - url: "../images/buildroot-bzimage.bin", - size: 5166352, + url: "../images/buildroot-bzimage68.bin", async: false, }, filesystem: {}, diff --git a/src/browser/main.js b/src/browser/main.js index 6f0d3c8386..78661856b7 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -463,6 +463,17 @@ filesystem: {}, cmdline: "tsc=reliable mitigations=off random.trust_cpu=on", }, + { + id: "buildroot6", + bzimage: { + url: host + "buildroot-bzimage68.bin", + size: 10068480, + async: false, + }, + name: "Buildroot Linux", + filesystem: {}, + cmdline: "tsc=reliable mitigations=off random.trust_cpu=on", + }, { id: "basiclinux", hda: { diff --git a/tests/api/state.js b/tests/api/state.js index e9afd5116c..f6176d081e 100755 --- a/tests/api/state.js +++ b/tests/api/state.js @@ -14,7 +14,7 @@ const config_async_cdrom = { vga_bios: { url: __dirname + "/../../bios/vgabios.bin" }, cdrom: { url: __dirname + "/../../images/linux4.iso", async: true }, autostart: true, - memory_size: 32 * 1024 * 1024, + memory_size: 64 * 1024 * 1024, filesystem: {}, screen_dummy: true, disable_jit: +process.env.DISABLE_JIT, @@ -26,7 +26,7 @@ const config_sync_cdrom = { vga_bios: { url: __dirname + "/../../bios/vgabios.bin" }, cdrom: { url: __dirname + "/../../images/linux4.iso", async: false }, autostart: true, - memory_size: 32 * 1024 * 1024, + memory_size: 64 * 1024 * 1024, filesystem: {}, screen_dummy: true, disable_jit: +process.env.DISABLE_JIT, @@ -37,9 +37,9 @@ const config_filesystem = { bios: { url: __dirname + "/../../bios/seabios.bin" }, vga_bios: { url: __dirname + "/../../bios/vgabios.bin" }, autostart: true, - memory_size: 32 * 1024 * 1024, + memory_size: 64 * 1024 * 1024, filesystem: {}, - bzimage: { url: __dirname + "/../../images/buildroot-bzimage.bin" }, + bzimage: { url: __dirname + "/../../images/buildroot-bzimage68.bin" }, cmdline: "tsc=reliable mitigations=off random.trust_cpu=on", network_relay_url: "", screen_dummy: true, diff --git a/tests/full/run.js b/tests/full/run.js index a78a715edc..72b52fdb4e 100755 --- a/tests/full/run.js +++ b/tests/full/run.js @@ -320,7 +320,7 @@ if(cluster.isMaster) }, { name: "Linux bzImage", - bzimage: root_path + "/images/buildroot-bzimage.bin", + bzimage: root_path + "/images/buildroot-bzimage68.bin", cmdline: "auto", timeout: 200, expected_texts: [ From c1c96dabf55629de02514ffaaed82510c31bc675 Mon Sep 17 00:00:00 2001 From: Fabian Date: Sun, 1 Sep 2024 15:28:31 +0200 Subject: [PATCH 24/39] update windows 95 image (with age of empires!) thanks @SuperMaxusa --- src/browser/main.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/browser/main.js b/src/browser/main.js index 78661856b7..640f42afbe 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -802,23 +802,32 @@ }, { id: "windows95", - memory_size: 32 * 1024 * 1024, + memory_size: 64 * 1024 * 1024, + // old image: + //memory_size: 32 * 1024 * 1024, + //hda: { + // url: host + "w95/.img", + // size: 242049024, + // async: true, + // fixed_chunk_size: 256 * 1024, + // use_parts: true, + //}, + //state: { url: host + "windows95_state.bin.zst" }, hda: { - url: host + "w95/.img", - size: 242049024, + url: host + "windows95-v2/.img", + size: 471859200, async: true, fixed_chunk_size: 256 * 1024, use_parts: true, }, name: "Windows 95", - state: { url: host + "windows95_state.bin.zst" }, }, { id: "windows95-boot", - memory_size: 32 * 1024 * 1024, + memory_size: 64 * 1024 * 1024, hda: { - url: host + "w95/.img", - size: 242049024, + url: host + "windows95-v2/.img", + size: 471859200, async: true, fixed_chunk_size: 256 * 1024, use_parts: true, From 9ec5e9640b516e7a7e819e06b5cf6f70cc85792b Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 3 Sep 2024 17:21:40 +0200 Subject: [PATCH 25/39] add 86dos thanks @SuperMaxusa --- src/browser/main.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/browser/main.js b/src/browser/main.js index 640f42afbe..a5da5bf8b5 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -398,6 +398,15 @@ name: "PsychDOS", homepage: "https://psychoslinux.gitlab.io/DOS/INDEX.HTM", }, + { + id: "86dos", + fda: { + url: host + "pc86dos.img", + size: 163840, + }, + name: "86-DOS", + homepage: "https://www.os2museum.com/wp/pc-86-dos/", + }, { id: "oberon", hda: { From a371aaffd4112b34667ce95b0b7c06ec391d2dbe Mon Sep 17 00:00:00 2001 From: Fabian Date: Sun, 8 Sep 2024 11:12:33 -0600 Subject: [PATCH 26/39] remove useless internal events (cpu-{init,run,stop,restart}) --- src/browser/starter.js | 10 +++++----- src/main.js | 4 ---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/browser/starter.js b/src/browser/starter.js index 1925e23c96..edde1cd512 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -602,7 +602,7 @@ V86.prototype.continue_init = async function(emulator, options) this.serial_adapter && this.serial_adapter.show && this.serial_adapter.show(); - this.bus.send("cpu-init", settings); + this.v86.init(settings); if(settings.initial_state) { @@ -616,7 +616,7 @@ V86.prototype.continue_init = async function(emulator, options) if(options.autostart) { - this.bus.send("cpu-run"); + this.v86.run(); } this.emulator_bus.send("emulator-loaded"); @@ -767,7 +767,7 @@ V86.prototype.get_bzimage_initrd_from_filesystem = function(filesystem) */ V86.prototype.run = async function() { - this.bus.send("cpu-run"); + this.v86.run(); }; /** @@ -787,7 +787,7 @@ V86.prototype.stop = async function() resolve(); }; this.add_listener("emulator-stopped", listener); - this.bus.send("cpu-stop"); + this.v86.stop(); }); }; @@ -814,7 +814,7 @@ V86.prototype.destroy = async function() */ V86.prototype.restart = function() { - this.bus.send("cpu-restart"); + this.v86.restart(); }; /** diff --git a/src/main.js b/src/main.js index 0872500786..34a2f255cd 100644 --- a/src/main.js +++ b/src/main.js @@ -22,10 +22,6 @@ function v86(bus, wasm) this.cpu = new CPU(bus, wasm, () => { this.idle && this.next_tick(0); }); this.bus = bus; - bus.register("cpu-init", this.init, this); - bus.register("cpu-run", this.run, this); - bus.register("cpu-stop", this.stop, this); - bus.register("cpu-restart", this.restart, this); this.register_yield(); } From a2272b27b2e3a019f851c3b4dfb5de2a69a21579 Mon Sep 17 00:00:00 2001 From: Fabian Date: Mon, 9 Sep 2024 18:13:49 -0600 Subject: [PATCH 27/39] add nanoshell, catk and mcp thanks @SuperMaxusa --- src/browser/main.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/browser/main.js b/src/browser/main.js index a5da5bf8b5..5dda75b952 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -1189,6 +1189,33 @@ }, homepage: "https://t3x.org/t3xforth/", }, + { + id: "nanoshell", + name: "NanoShell", + cdrom: { + url: host + "nanoshell.iso", + size: 6785024, + }, + homepage: "https://github.com/iProgramMC/NanoShellOS", + }, + { + id: "catk", + name: "CatK", + cdrom: { + url: host + "catkernel.iso", + size: 11968512, + }, + homepage: "https://catk.neocities.org/", + }, + { + id: "mcp", + name: "M/CP", + fda: { + url: host + "mcp2.img", + size: 512, + }, + homepage: "https://github.com/ybuzoku/MCP", + }, ]; if(DEBUG) From 6e4f6754b64d3eeb399c1f9146c12325227c0635 Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 11 Sep 2024 17:13:30 -0600 Subject: [PATCH 28/39] enable screen dummy by default There is essentially no overhead to this, and it allows some further simplifications --- src/browser/starter.js | 2 +- tests/api/clean-shutdown.js | 1 - tests/api/floppy-insert-eject.js | 1 - tests/api/reset.js | 1 - tests/api/serial.js | 1 - tests/api/state.js | 4 ---- tests/api/test.js | 1 - tests/benchmark/arch-bytemark.js | 1 - tests/benchmark/arch-python.js | 1 - tests/benchmark/linux-boot.js | 1 - tools/docker/alpine/build-state.js | 1 - 11 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/browser/starter.js b/src/browser/starter.js index edde1cd512..8e3a1b867a 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -323,7 +323,7 @@ V86.prototype.continue_init = async function(emulator, options) { this.screen_adapter = new ScreenAdapter(options.screen_container, this.bus); } - else if(options.screen_dummy) + else { this.screen_adapter = new DummyScreenAdapter(this.bus); } diff --git a/tests/api/clean-shutdown.js b/tests/api/clean-shutdown.js index 94489516e0..5e011f9cfb 100755 --- a/tests/api/clean-shutdown.js +++ b/tests/api/clean-shutdown.js @@ -21,7 +21,6 @@ const config = { filesystem: {}, log_level: 0, disable_jit: +process.env.DISABLE_JIT, - screen_dummy: true, }; const emulator = new V86(config); diff --git a/tests/api/floppy-insert-eject.js b/tests/api/floppy-insert-eject.js index 313bc166a8..0470948c19 100755 --- a/tests/api/floppy-insert-eject.js +++ b/tests/api/floppy-insert-eject.js @@ -18,7 +18,6 @@ const emulator = new V86({ filesystem: {}, log_level: 0, disable_jit: +process.env.DISABLE_JIT, - screen_dummy: true, }); emulator.automatically([ diff --git a/tests/api/reset.js b/tests/api/reset.js index 84f2bdf85e..d4657dd8cf 100755 --- a/tests/api/reset.js +++ b/tests/api/reset.js @@ -20,7 +20,6 @@ const config = { filesystem: {}, log_level: 0, disable_jit: +process.env.DISABLE_JIT, - screen_dummy: true, }; const emulator = new V86(config); diff --git a/tests/api/serial.js b/tests/api/serial.js index c85080e9e3..68ff868ca4 100755 --- a/tests/api/serial.js +++ b/tests/api/serial.js @@ -20,7 +20,6 @@ const config = { filesystem: {}, log_level: 0, disable_jit: +process.env.DISABLE_JIT, - screen_dummy: true, }; const emulator = new V86(config); diff --git a/tests/api/state.js b/tests/api/state.js index f6176d081e..bfaf859be8 100755 --- a/tests/api/state.js +++ b/tests/api/state.js @@ -16,7 +16,6 @@ const config_async_cdrom = { autostart: true, memory_size: 64 * 1024 * 1024, filesystem: {}, - screen_dummy: true, disable_jit: +process.env.DISABLE_JIT, log_level: 0, }; @@ -28,7 +27,6 @@ const config_sync_cdrom = { autostart: true, memory_size: 64 * 1024 * 1024, filesystem: {}, - screen_dummy: true, disable_jit: +process.env.DISABLE_JIT, log_level: 0, }; @@ -42,7 +40,6 @@ const config_filesystem = { bzimage: { url: __dirname + "/../../images/buildroot-bzimage68.bin" }, cmdline: "tsc=reliable mitigations=off random.trust_cpu=on", network_relay_url: "", - screen_dummy: true, disable_jit: +process.env.DISABLE_JIT, log_level: 0, }; @@ -55,7 +52,6 @@ const config_large_memory = { memory_size: 2048 * 1024 * 1024, vga_memory_size: 512 * 1024 * 1024, network_relay_url: "", - screen_dummy: true, disable_jit: +process.env.DISABLE_JIT, log_level: 0, }; diff --git a/tests/api/test.js b/tests/api/test.js index f93dbf7000..1b12dcb488 100755 --- a/tests/api/test.js +++ b/tests/api/test.js @@ -18,7 +18,6 @@ const emulator = new V86({ filesystem: {}, log_level: -641, disable_jit: +process.env.DISABLE_JIT, - screen_dummy: true, }); setInterval(() => { diff --git a/tests/benchmark/arch-bytemark.js b/tests/benchmark/arch-bytemark.js index 371a85709c..887d6c91c8 100755 --- a/tests/benchmark/arch-bytemark.js +++ b/tests/benchmark/arch-bytemark.js @@ -17,7 +17,6 @@ const emulator = new V86({ network_relay_url: "", initial_state: { url: path.join(V86_ROOT, "/images/arch_state.bin") }, filesystem: { baseurl: path.join(V86_ROOT, "/images/arch/") }, - screen_dummy: true, disable_jit: +process.env.DISABLE_JIT, log_level: 0, }); diff --git a/tests/benchmark/arch-python.js b/tests/benchmark/arch-python.js index 3b0486d00d..c68e6120be 100755 --- a/tests/benchmark/arch-python.js +++ b/tests/benchmark/arch-python.js @@ -17,7 +17,6 @@ const emulator = new V86({ network_relay_url: "", initial_state: { url: path.join(V86_ROOT, "/images/arch_state.bin") }, filesystem: { baseurl: path.join(V86_ROOT, "/images/arch/") }, - screen_dummy: true, disable_jit: +process.env.DISABLE_JIT, log_level: 0, }); diff --git a/tests/benchmark/linux-boot.js b/tests/benchmark/linux-boot.js index 0fb7c1410c..07e6c1b602 100755 --- a/tests/benchmark/linux-boot.js +++ b/tests/benchmark/linux-boot.js @@ -40,7 +40,6 @@ else }, baseurl: path.join(V86_ROOT, "/images/arch/"), }, - screen_dummy: true, disable_jit: +process.env.DISABLE_JIT, log_level: 0, }); diff --git a/tools/docker/alpine/build-state.js b/tools/docker/alpine/build-state.js index 17d35ef917..2a2f3341f8 100755 --- a/tools/docker/alpine/build-state.js +++ b/tools/docker/alpine/build-state.js @@ -23,7 +23,6 @@ var emulator = new V86({ baseurl: path.join(V86_ROOT, "images/alpine-rootfs-flat"), basefs: path.join(V86_ROOT, "images/alpine-fs.json"), }, - screen_dummy: true, }); console.log("Now booting, please stand by ..."); From 860de55db89b843618bda139b0f6245b8da8e301 Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 13 Sep 2024 13:32:26 -0600 Subject: [PATCH 29/39] refactor vga code - stop using bus for internal calls - replace emulator.automatically with emulator.wait_until_vga_screen_contains - remove stats - remove graphical_mode_is_linear (unused) --- debug.html | 1 - index.html | 1 - src/browser/dummy_screen.js | 116 ++++----------------- src/browser/main.js | 20 +--- src/browser/screen.js | 138 ++++++++++++------------- src/browser/starter.js | 74 +++++++++++--- src/cpu.js | 6 +- src/vga.js | 169 ++++++++++++++----------------- tests/api/floppy-insert-eject.js | 35 +++---- tests/api/serial.js | 12 +-- tests/api/state.js | 16 +-- tests/full/run.js | 15 ++- 12 files changed, 256 insertions(+), 347 deletions(-) diff --git a/debug.html b/debug.html index 742096260e..5dbca6a9cd 100644 --- a/debug.html +++ b/debug.html @@ -329,7 +329,6 @@

Debugger

VGA
Mode:
Resolution: -
- BPP: -

Mouse: No
diff --git a/index.html b/index.html index e3e6622641..271193b3fc 100644 --- a/index.html +++ b/index.html @@ -246,7 +246,6 @@

Setup

VGA
Mode:
Resolution: -
- BPP: -

Mouse: No
diff --git a/src/browser/dummy_screen.js b/src/browser/dummy_screen.js index 6ab93d3f52..2f7757d0b0 100644 --- a/src/browser/dummy_screen.js +++ b/src/browser/dummy_screen.js @@ -2,22 +2,20 @@ /** * @constructor - * - * @param {BusConnector} bus */ -function DummyScreenAdapter(bus) +function DummyScreenAdapter() { var graphic_image_data, /** @type {number} */ - cursor_row, + cursor_row = 0, /** @type {number} */ - cursor_col, + cursor_col = 0, - graphical_mode_width, - graphical_mode_height, + graphical_mode_width = 0, + graphical_mode_height = 0, // are we in graphical mode now? is_graphical = false, @@ -29,77 +27,21 @@ function DummyScreenAdapter(bus) text_mode_data, // number of columns - text_mode_width, + text_mode_width = 0, // number of rows - text_mode_height; - - const CHARACTER_INDEX = 0; - const BLINKING_INDEX = 1; - const BG_COLOR_INDEX = 2; - const FG_COLOR_INDEX = 3; - const TEXT_MODE_COMPONENT_SIZE = 4; - - this.bus = bus; - - bus.register("screen-set-mode", function(data) - { - this.set_mode(data); - }, this); - - bus.register("screen-fill-buffer-end", function(data) - { - var min = data[0]; - var max = data[1]; - - this.update_buffer(min, max); - }, this); - - bus.register("screen-put-char", function(data) - { - //console.log(data); - this.put_char(data[0], data[1], data[2], data[3], data[4], data[5]); - }, this); - - bus.register("screen-text-scroll", function(rows) - { - console.log("scroll", rows); - }, this); - - bus.register("screen-update-cursor", function(data) - { - this.update_cursor(data[0], data[1]); - }, this); - bus.register("screen-update-cursor-scanline", function(data) - { - this.update_cursor_scanline(data[0], data[1], data[2]); - }, this); - - bus.register("screen-set-size-text", function(data) - { - this.set_size_text(data[0], data[1]); - }, this); - bus.register("screen-set-size-graphical", function(data) - { - this.set_size_graphical(data[0], data[1]); - }, this); + text_mode_height = 0; this.put_char = function(row, col, chr, blinking, bg_color, fg_color) { - if(row < text_mode_height && col < text_mode_width) - { - var p = TEXT_MODE_COMPONENT_SIZE * (row * text_mode_width + col); - - text_mode_data[p + CHARACTER_INDEX] = chr; - text_mode_data[p + BLINKING_INDEX] = blinking; - text_mode_data[p + BG_COLOR_INDEX] = bg_color; - text_mode_data[p + FG_COLOR_INDEX] = fg_color; - } + dbg_assert(row >= 0 && row < text_mode_height); + dbg_assert(col >= 0 && col < text_mode_width); + text_mode_data[row * text_mode_width + col] = chr; }; - this.destroy = function() - { - }; + this.destroy = function() {}; + this.pause = function() {}; + this.continue = function() {}; this.set_mode = function(graphical) { @@ -121,8 +63,7 @@ function DummyScreenAdapter(bus) return; } - text_mode_data = new Int32Array(cols * rows * TEXT_MODE_COMPONENT_SIZE); - + text_mode_data = new Uint8Array(cols * rows); text_mode_width = cols; text_mode_height = rows; }; @@ -143,22 +84,12 @@ function DummyScreenAdapter(bus) this.update_cursor = function(row, col) { - if(row !== cursor_row || col !== cursor_col) - { - cursor_row = row; - cursor_col = col; - } + cursor_row = row; + cursor_col = col; }; - this.update_buffer = function(min, max) + this.update_buffer = function(layers) { - if(max < min) - { - return; - } - - var min_y = min / graphical_mode_width | 0; - var max_y = max / graphical_mode_width | 0; }; this.get_text_screen = function() @@ -175,14 +106,9 @@ function DummyScreenAdapter(bus) this.get_text_row = function(i) { - var row = ""; - var offset = TEXT_MODE_COMPONENT_SIZE * i * text_mode_width; - - for(var j = 0; j < text_mode_width; j++) - { - row += String.fromCharCode(text_mode_data[offset + CHARACTER_INDEX + TEXT_MODE_COMPONENT_SIZE * j]); - } - - return row; + const offset = i * text_mode_width; + return String.fromCharCode.apply(String, text_mode_data.subarray(offset, offset + text_mode_width)); }; + + this.set_size_text(80, 25); } diff --git a/src/browser/main.js b/src/browser/main.js index 5dda75b952..a2d6ebcde9 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -1995,23 +1995,11 @@ $("info_mouse_enabled").textContent = is_enabled ? "Yes" : "No"; }); - emulator.add_listener("screen-set-mode", function(is_graphical) + emulator.add_listener("screen-set-size", function(args) { - 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]; + const [w, h, bpp] = args; + $("info_res").textContent = w + "x" + h + (bpp ? "x" + bpp : ""); + $("info_vga_mode").textContent = bpp ? "Graphical" : "Text"; }); diff --git a/src/browser/screen.js b/src/browser/screen.js index fb72ec1b13..351dc94972 100644 --- a/src/browser/screen.js +++ b/src/browser/screen.js @@ -3,11 +3,11 @@ /** * Adapter to use visual screen in browsers (in contrast to node) * @constructor - * - * @param {BusConnector} bus */ -function ScreenAdapter(screen_container, bus) +function ScreenAdapter(screen_container, screen_fill_buffer) { + this.screen_fill_buffer = screen_fill_buffer; + console.assert(screen_container, "1st argument must be a DOM container"); var @@ -56,8 +56,7 @@ function ScreenAdapter(screen_container, bus) const TEXT_MODE_COMPONENT_SIZE = 4; var stopped = false; - - var screen = this; + var paused = false; // 0x12345 -> "#012345" function number_as_color(n) @@ -131,48 +130,6 @@ function ScreenAdapter(screen_container, bus) text_screen.style.display = "block"; graphic_screen.style.display = "none"; - this.bus = bus; - - bus.register("screen-set-mode", function(data) - { - this.set_mode(data); - }, this); - - bus.register("screen-fill-buffer-end", function(data) - { - this.update_buffer(data); - }, this); - - bus.register("screen-put-char", function(data) - { - //console.log(data); - this.put_char(data[0], data[1], data[2], data[3], data[4], data[5]); - }, this); - - bus.register("screen-update-cursor", function(data) - { - this.update_cursor(data[0], data[1]); - }, this); - bus.register("screen-update-cursor-scanline", function(data) - { - this.update_cursor_scanline(data[0], data[1], data[2]); - }, this); - - bus.register("screen-clear", function() - { - this.clear_screen(); - }, this); - - bus.register("screen-set-size-text", function(data) - { - this.set_size_text(data[0], data[1]); - }, this); - bus.register("screen-set-size-graphical", function(data) - { - this.set_size_graphical(data[0], data[1], data[2], data[3]); - }, this); - - this.init = function() { // not necessary, because this gets initialized by the bios early, @@ -203,9 +160,9 @@ function ScreenAdapter(screen_container, bus) context.font = window.getComputedStyle(text_screen).font; context.textBaseline = "top"; - for(let x = 0; x < text_mode_width; x++) + for(let y = 0; y < text_mode_height; y++) { - for(let y = 0; y < text_mode_height; y++) + for(let x = 0; x < text_mode_width; x++) { const index = (y * text_mode_width + x) * TEXT_MODE_COMPONENT_SIZE; const character = text_mode_data[index + CHARACTER_INDEX]; @@ -237,62 +194,66 @@ function ScreenAdapter(screen_container, bus) this.put_char = function(row, col, chr, blinking, bg_color, fg_color) { - if(row < text_mode_height && col < text_mode_width) - { - var p = TEXT_MODE_COMPONENT_SIZE * (row * text_mode_width + col); + dbg_assert(row >= 0 && row < text_mode_height); + dbg_assert(col >= 0 && col < text_mode_width); + dbg_assert(chr >= 0 && chr < 0x100); - dbg_assert(chr >= 0 && chr < 0x100); - text_mode_data[p + CHARACTER_INDEX] = chr; - text_mode_data[p + BLINKING_INDEX] = blinking; - text_mode_data[p + BG_COLOR_INDEX] = bg_color; - text_mode_data[p + FG_COLOR_INDEX] = fg_color; + const p = TEXT_MODE_COMPONENT_SIZE * (row * text_mode_width + col); - changed_rows[row] = 1; - } + text_mode_data[p + CHARACTER_INDEX] = chr; + text_mode_data[p + BLINKING_INDEX] = blinking; + text_mode_data[p + BG_COLOR_INDEX] = bg_color; + text_mode_data[p + FG_COLOR_INDEX] = fg_color; + + changed_rows[row] = 1; }; this.timer = function() { if(!stopped) { - requestAnimationFrame(is_graphical ? update_graphical : update_text); + requestAnimationFrame(() => is_graphical ? this.update_graphical() : this.update_text()); } }; - var update_text = function() + this.update_text = function() { for(var i = 0; i < text_mode_height; i++) { if(changed_rows[i]) { - screen.text_update_row(i); + this.text_update_row(i); changed_rows[i] = 0; } } this.timer(); - }.bind(this); + }; - var update_graphical = function() + this.update_graphical = function() { - this.bus.send("screen-fill-buffer"); + if(!paused) + { + this.screen_fill_buffer(); + } this.timer(); - }.bind(this); + }; this.destroy = function() { stopped = true; }; - // TODO: Should probably not use bus for this - this.bus.register("emulator-stopped", function() + this.pause = function() { + paused = true; cursor_element.classList.remove("blinking-cursor"); - }, this); - this.bus.register("emulator-started", function() + }; + this.continue = function() { + paused = false; cursor_element.classList.add("blinking-cursor"); - }, this); + }; this.set_mode = function(graphical) { @@ -569,7 +530,7 @@ function ScreenAdapter(screen_container, bus) // rectangle to visualise the layer instead. graphic_context.strokeStyle = "#0F0"; graphic_context.lineWidth = 4; - layers.forEach(layer => + for(const layer of layers) { graphic_context.strokeRect( layer.buffer_x, @@ -577,12 +538,12 @@ function ScreenAdapter(screen_container, bus) layer.buffer_width, layer.buffer_height ); - }); + } graphic_context.lineWidth = 1; return; } - layers.forEach(layer => + for(const layer of layers) { graphic_context.putImageData( layer.image_data, @@ -593,7 +554,34 @@ function ScreenAdapter(screen_container, bus) layer.buffer_width, layer.buffer_height ); - }); + } + }; + + // XXX: duplicated in DummyScreenAdapter + this.get_text_screen = function() + { + var screen = []; + + for(var i = 0; i < text_mode_height; i++) + { + screen.push(this.get_text_row(i)); + } + + return screen; + }; + + this.get_text_row = function(y) + { + let result = ""; + + for(let x = 0; x < text_mode_width; x++) + { + const index = (y * text_mode_width + x) * TEXT_MODE_COMPONENT_SIZE; + const character = text_mode_data[index + CHARACTER_INDEX]; + result += String.fromCharCode(character); + } + + return result; }; this.init(); diff --git a/src/browser/starter.js b/src/browser/starter.js index 8e3a1b867a..0054fb2cc1 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -247,11 +247,13 @@ V86.prototype.continue_init = async function(emulator, options) this.bus.register("emulator-stopped", function() { this.cpu_is_running = false; + this.screen_adapter.pause(); }, this); this.bus.register("emulator-started", function() { this.cpu_is_running = true; + this.screen_adapter.continue(); }, this); var settings = {}; @@ -321,12 +323,13 @@ V86.prototype.continue_init = async function(emulator, options) if(options.screen_container) { - this.screen_adapter = new ScreenAdapter(options.screen_container, this.bus); + this.screen_adapter = new ScreenAdapter(options.screen_container, () => this.v86.cpu.devices.vga.screen_fill_buffer()); } else { - this.screen_adapter = new DummyScreenAdapter(this.bus); + this.screen_adapter = new DummyScreenAdapter(); } + settings.screen = this.screen_adapter; if(options.serial_container) { @@ -824,7 +827,7 @@ V86.prototype.restart = function() * The callback function gets a single argument which depends on the event. * * @param {string} event Name of the event. - * @param {function(*)} listener The callback function. + * @param {function(?)} listener The callback function. * @export */ V86.prototype.add_listener = function(event, listener) @@ -1277,6 +1280,10 @@ V86.prototype.read_file = async function(file) } }; +/* + * @deprecated + * Use wait_until_vga_screen_contains etc. + */ V86.prototype.automatically = function(steps) { const run = (steps) => @@ -1298,18 +1305,7 @@ V86.prototype.automatically = function(steps) if(step.vga_text) { - const screen = this.screen_adapter.get_text_screen(); - - for(const line of screen) - { - if(line.includes(step.vga_text)) - { - run(remaining_steps); - return; - } - } - - setTimeout(() => run(steps), 1000); + this.wait_until_vga_screen_contains(step.vga_text).then(() => run(remaining_steps)); return; } @@ -1342,6 +1338,54 @@ V86.prototype.automatically = function(steps) run(steps); }; +V86.prototype.wait_until_vga_screen_contains = function(text) +{ + return new Promise(resolve => + { + function test_line(line) + { + return typeof text === "string" ? line.includes(text) : text.test(line); + } + + for(const line of this.screen_adapter.get_text_screen()) + { + if(test_line(line)) + { + resolve(true); + return; + } + } + + const changed_rows = new Set(); + + function put_char(args) + { + const [row, col, char] = args; + changed_rows.add(col); + } + + const check = () => + { + for(const row of changed_rows) + { + const line = this.screen_adapter.get_text_row(row); + if(test_line(line)) + { + this.remove_listener("screen-put-char", put_char); + resolve(); + return; + } + } + + changed_rows.clear(); + setTimeout(check, 100); + }; + check(); + + this.add_listener("screen-put-char", put_char); + }); +}; + /** * Reads data from memory at specified offset. * diff --git a/src/cpu.js b/src/cpu.js index cc7d69c213..5334af70f0 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -729,6 +729,9 @@ CPU.prototype.create_memory = function(size, minimum_size) this.mem32s = v86util.view(Uint32Array, this.wasm_memory, memory_offset, size >> 2); }; +/** + * @param {BusConnector} device_bus + */ CPU.prototype.init = function(settings, device_bus) { this.create_memory( @@ -931,8 +934,7 @@ CPU.prototype.init = function(settings, device_bus) this.devices.dma = new DMA(this); - this.devices.vga = new VGAScreen(this, device_bus, - settings.vga_memory_size || 8 * 1024 * 1024); + this.devices.vga = new VGAScreen(this, device_bus, settings.screen, settings.vga_memory_size || 8 * 1024 * 1024); this.devices.ps2 = new PS2(this, device_bus); diff --git a/src/vga.js b/src/vga.js index 0f2f1de272..928b77d847 100644 --- a/src/vga.js +++ b/src/vga.js @@ -1,47 +1,32 @@ "use strict"; +// Always 64k +const VGA_BANK_SIZE = 64 * 1024; -var - /** - * Always 64k - * @const - */ - VGA_BANK_SIZE = 64 * 1024, +const MAX_XRES = 2560; +const MAX_YRES = 1600; +const MAX_BPP = 32; - /** @const */ - MAX_XRES = 2560, - - /** @const */ - MAX_YRES = 1600, - - /** @const */ - MAX_BPP = 32; - -/** @const */ -//var VGA_LFB_ADDRESS = 0xFE000000; // set by seabios -var VGA_LFB_ADDRESS = 0xE0000000; +//const VGA_LFB_ADDRESS = 0xFE000000; // set by seabios +const VGA_LFB_ADDRESS = 0xE0000000; /** - * @const * Equals the maximum number of pixels for non svga. * 8 pixels per byte. */ -var VGA_PIXEL_BUFFER_SIZE = 8 * VGA_BANK_SIZE; +const VGA_PIXEL_BUFFER_SIZE = 8 * VGA_BANK_SIZE; -/** @const */ -var VGA_MIN_MEMORY_SIZE = 4 * VGA_BANK_SIZE; +const VGA_MIN_MEMORY_SIZE = 4 * VGA_BANK_SIZE; /** * Avoid wrapping past VGA_LFB_ADDRESS - * @const */ -var VGA_MAX_MEMORY_SIZE = 256 * 1024 * 1024; +const VGA_MAX_MEMORY_SIZE = 256 * 1024 * 1024; /** - * @const * @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#06} */ -var VGA_HOST_MEMORY_SPACE_START = Uint32Array.from([ +const VGA_HOST_MEMORY_SPACE_START = Uint32Array.from([ 0xA0000, 0xA0000, 0xB0000, @@ -52,7 +37,7 @@ var VGA_HOST_MEMORY_SPACE_START = Uint32Array.from([ * @const * @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#06} */ -var VGA_HOST_MEMORY_SPACE_SIZE = Uint32Array.from([ +const VGA_HOST_MEMORY_SPACE_SIZE = Uint32Array.from([ 0x20000, // 128K 0x10000, // 64K 0x8000, // 32K @@ -63,15 +48,19 @@ var VGA_HOST_MEMORY_SPACE_SIZE = Uint32Array.from([ * @constructor * @param {CPU} cpu * @param {BusConnector} bus + * @param {ScreenAdapter|DummyScreenAdapter} screen * @param {number} vga_memory_size */ -function VGAScreen(cpu, bus, vga_memory_size) +function VGAScreen(cpu, bus, screen, vga_memory_size) { this.cpu = cpu; - /** @const @type {BusConnector} */ + /** @const */ this.bus = bus; + /** @const */ + this.screen = screen; + this.vga_memory_size = vga_memory_size; /** @type {number} */ @@ -175,15 +164,9 @@ function VGAScreen(cpu, bus, vga_memory_size) // End of CRTC registers - /** - * Used for svga, e.g. banked modes - * @type{boolean} - */ - this.graphical_mode_is_linear = true; - /** @type {boolean} */ this.graphical_mode = false; - setTimeout(() => { bus.send("screen-set-mode", this.graphical_mode); }, 0); + this.screen.set_mode(this.graphical_mode); /* * VGA palette containing 256 colors for video mode 13, svga 8bpp, etc. @@ -253,13 +236,6 @@ function VGAScreen(cpu, bus, vga_memory_size) this.name = "vga"; - this.stats = { - is_graphical: false, - res_x: 0, - res_y: 0, - bpp: 0, - }; - this.index_crtc = 0; // index for setting colors through port 3C9h @@ -391,11 +367,6 @@ function VGAScreen(cpu, bus, vga_memory_size) this.image_data = null; - bus.register("screen-fill-buffer", function() - { - this.screen_fill_buffer(); - }, this); - this.vga_memory = new Uint8Array(4 * VGA_BANK_SIZE); this.plane0 = new Uint8Array(this.vga_memory.buffer, 0 * VGA_BANK_SIZE, VGA_BANK_SIZE); this.plane1 = new Uint8Array(this.vga_memory.buffer, 1 * VGA_BANK_SIZE, VGA_BANK_SIZE); @@ -403,10 +374,9 @@ function VGAScreen(cpu, bus, vga_memory_size) this.plane3 = new Uint8Array(this.vga_memory.buffer, 3 * VGA_BANK_SIZE, VGA_BANK_SIZE); this.pixel_buffer = new Uint8Array(VGA_PIXEL_BUFFER_SIZE); - var me = this; io.mmap_register(0xA0000, 0x20000, - function(addr) { return me.vga_memory_read(addr); }, - function(addr, value) { me.vga_memory_write(addr, value); } + addr => this.vga_memory_read(addr), + (addr, value) => this.vga_memory_write(addr, value), ); cpu.devices.pci.register_device(this); @@ -456,7 +426,7 @@ VGAScreen.prototype.get_state = function() state[37] = this.dispi_index; state[38] = this.dispi_enable_value; state[39] = this.svga_memory; - state[40] = this.graphical_mode_is_linear; + // this.graphical_mode_is_linear state[41] = this.attribute_controller_index; state[42] = this.offset_register; state[43] = this.planar_setreset; @@ -525,7 +495,7 @@ VGAScreen.prototype.set_state = function(state) this.dispi_index = state[37]; this.dispi_enable_value = state[38]; this.svga_memory.set(state[39]); - this.graphical_mode_is_linear = state[40]; + // state[40]; this.attribute_controller_index = state[41]; this.offset_register = state[42]; this.planar_setreset = state[43]; @@ -549,7 +519,7 @@ VGAScreen.prototype.set_state = function(state) state[61] && this.pixel_buffer.set(state[61]); this.dac_mask = state[62] === undefined ? 0xFF : state[62]; - this.bus.send("screen-set-mode", this.graphical_mode); + this.screen.set_mode(this.graphical_mode); if(this.graphical_mode) { @@ -559,8 +529,7 @@ VGAScreen.prototype.set_state = function(state) if(this.svga_enabled) { - this.set_size_graphical(this.svga_width, this.svga_height, - this.svga_bpp, this.svga_width, this.svga_height); + this.set_size_graphical(this.svga_width, this.svga_height, this.svga_width, this.svga_height, this.svga_bpp); this.update_layers(); } else @@ -581,8 +550,9 @@ VGAScreen.prototype.set_state = function(state) VGAScreen.prototype.vga_memory_read = function(addr) { - if(this.svga_enabled && this.graphical_mode_is_linear) + if(this.svga_enabled) { + // vbe banked mode (accessing svga memory through the regular vga memory range) return this.cpu.read8((addr - 0xA0000 | this.svga_bank_offset) + VGA_LFB_ADDRESS | 0); } @@ -653,9 +623,9 @@ VGAScreen.prototype.vga_memory_read = function(addr) VGAScreen.prototype.vga_memory_write = function(addr, value) { - if(this.svga_enabled && this.graphical_mode && this.graphical_mode_is_linear) + if(this.svga_enabled) { - // vbe banked mode + // vbe banked mode (accessing svga memory through the regular vga memory range) this.cpu.write8((addr - 0xA0000 | this.svga_bank_offset) + VGA_LFB_ADDRESS | 0, value); return; } @@ -852,32 +822,31 @@ VGAScreen.prototype.apply_bitmask = function(data_dword, bitmask_dword) VGAScreen.prototype.text_mode_redraw = function() { - var addr = this.start_address << 1, - chr, - blinking, - color; - const split_screen_row = this.scan_line_to_screen_row(this.line_compare); const row_offset = Math.max(0, (this.offset_register * 2 - this.max_cols) * 2); const blink_flag = this.attribute_mode & 1 << 3; const bg_color_mask = blink_flag ? 7 : 0xF; - for(var row = 0; row < this.max_rows; row++) + let addr = this.start_address << 1; + + for(let row = 0; row < this.max_rows; row++) { if(row === split_screen_row) { addr = 0; } - for(var col = 0; col < this.max_cols; col++) + for(let col = 0; col < this.max_cols; col++) { - chr = this.vga_memory[addr]; - color = this.vga_memory[addr | 1]; - blinking = blink_flag && (color & 1 << 7); + const chr = this.vga_memory[addr]; + const color = this.vga_memory[addr | 1]; + const blinking = blink_flag && (color & 1 << 7); + + this.bus.send("screen-put-char", [row, col, chr]); - this.bus.send("screen-put-char", [row, col, chr, blinking, + this.screen.put_char(row, col, chr, blinking, this.vga256_palette[this.dac_mask & this.dac_map[color >> 4 & bg_color_mask]], - this.vga256_palette[this.dac_mask & this.dac_map[color & 0xF]]]); + this.vga256_palette[this.dac_mask & this.dac_map[color & 0xF]]); addr += 2; } @@ -932,9 +901,11 @@ VGAScreen.prototype.vga_memory_write_text_mode = function(addr, value) const blinking = blink_flag && (color & 1 << 7); const bg_color_mask = blink_flag ? 7 : 0xF; - this.bus.send("screen-put-char", [row, col, chr, blinking, + this.bus.send("screen-put-char", [row, col, chr]); + + this.screen.put_char(row, col, chr, blinking, this.vga256_palette[this.dac_mask & this.dac_map[color >> 4 & bg_color_mask]], - this.vga256_palette[this.dac_mask & this.dac_map[color & 0xF]]]); + this.vga256_palette[this.dac_mask & this.dac_map[color & 0xF]]); }; VGAScreen.prototype.update_cursor = function() @@ -957,7 +928,7 @@ VGAScreen.prototype.update_cursor = function() dbg_assert(row >= 0 && col >= 0); // NOTE: is allowed to be out of bounds - this.bus.send("screen-update-cursor", [row, col]); + this.screen.update_cursor(row, col); }; VGAScreen.prototype.complete_redraw = function() @@ -1146,19 +1117,22 @@ VGAScreen.prototype.scan_line_to_screen_row = function(scan_line) */ VGAScreen.prototype.set_size_text = function(cols_count, rows_count) { + dbg_assert(!this.graphical_mode); this.max_cols = cols_count; this.max_rows = rows_count; - this.bus.send("screen-set-size-text", [cols_count, rows_count]); + this.screen.set_size_text(cols_count, rows_count); + this.bus.send("screen-set-size", [cols_count, rows_count, 0]); }; -VGAScreen.prototype.set_size_graphical = function(width, height, bpp, virtual_width, virtual_height) +VGAScreen.prototype.set_size_graphical = function(width, height, virtual_width, virtual_height, bpp) { + dbg_assert(this.graphical_mode); + virtual_width = Math.max(virtual_width, 1); virtual_height = Math.max(virtual_height, 1); - var needs_update = !this.stats.is_graphical || - this.stats.bpp !== bpp || + const needs_update = this.screen_width !== width || this.screen_height !== height || this.virtual_width !== virtual_width || @@ -1171,11 +1145,6 @@ VGAScreen.prototype.set_size_graphical = function(width, height, bpp, virtual_wi this.virtual_width = virtual_width; this.virtual_height = virtual_height; - this.stats.bpp = bpp; - this.stats.is_graphical = true; - this.stats.res_x = width; - this.stats.res_y = height; - if(typeof ImageData !== "undefined") { const size = virtual_width * virtual_height; @@ -1191,7 +1160,8 @@ VGAScreen.prototype.set_size_graphical = function(width, height, bpp, virtual_wi // TODO: nodejs } - this.bus.send("screen-set-size-graphical", [width, height, virtual_width, virtual_height, bpp]); + this.screen.set_size_graphical(width, height, virtual_width, virtual_height); + this.bus.send("screen-set-size", [width, height, bpp]); } }; @@ -1245,7 +1215,7 @@ VGAScreen.prototype.update_vga_size = function() const bytes_per_line = this.vga_bytes_per_line(); const virtual_height = bytes_per_line ? Math.ceil(available_bytes / bytes_per_line) : screen_height; - this.set_size_graphical(screen_width, screen_height, 8, virtual_width, virtual_height); + this.set_size_graphical(screen_width, screen_height, virtual_width, virtual_height, 8); this.update_vertical_retrace(); this.update_layers(); @@ -1293,7 +1263,7 @@ VGAScreen.prototype.update_layers = function() // See http://www.phatcode.net/res/224/files/html/ch29/29-05.html#Heading6 // and http://www.osdever.net/FreeVGA/vga/seqreg.htm#01 this.layers = []; - this.bus.send("screen-clear"); + this.screen.clear_screen(); return; } @@ -1370,7 +1340,7 @@ VGAScreen.prototype.update_cursor_scanline = function() const start = Math.min(max, this.cursor_scanline_start & 0x1F); const end = Math.min(max, this.cursor_scanline_end & 0x1F); const visible = !disabled && start < end; - this.bus.send("screen-update-cursor-scanline", [start, end, visible]); + this.screen.update_cursor_scanline(start, end, visible); }; /** @@ -1421,7 +1391,7 @@ VGAScreen.prototype.port3C0_write = function(value) if(!this.svga_enabled && this.graphical_mode !== is_graphical) { this.graphical_mode = is_graphical; - this.bus.send("screen-set-mode", this.graphical_mode); + this.screen.set_mode(this.graphical_mode); } if((previous_mode ^ value) & 0x40) @@ -2079,6 +2049,8 @@ VGAScreen.prototype.port1CF_write = function(value) { dbg_log("1CF / dispi write " + h(this.dispi_index) + ": " + h(value), LOG_VGA); + const was_enabled = this.svga_enabled; + switch(this.dispi_index) { case 0: @@ -2159,17 +2131,24 @@ VGAScreen.prototype.port1CF_write = function(value) this.svga_bpp === 24 || this.svga_bpp === 32, "unexpected svga bpp: " + this.svga_bpp); - dbg_log("SVGA: enabled=" + this.svga_enabled + ", " + this.svga_width + "x" + this.svga_height + "x" + this.svga_bpp, LOG_VGA); + if(this.svga_enabled) + { + dbg_log("SVGA: enabled, " + this.svga_width + "x" + this.svga_height + "x" + this.svga_bpp, LOG_VGA); + } + else + { + dbg_log("SVGA: disabled"); + } - if(this.svga_enabled && this.dispi_index === 4) + if(this.svga_enabled && !was_enabled) { this.svga_offset = 0; this.svga_offset_x = 0; this.svga_offset_y = 0; - this.set_size_graphical(this.svga_width, this.svga_height, this.svga_bpp, this.svga_width, this.svga_height); - this.bus.send("screen-set-mode", true); + this.graphical_mode = true; - this.graphical_mode_is_linear = true; + this.screen.set_mode(this.graphical_mode); + this.set_size_graphical(this.svga_width, this.svga_height, this.svga_width, this.svga_height, this.svga_bpp); } if(!this.svga_enabled) @@ -2457,7 +2436,7 @@ VGAScreen.prototype.screen_fill_buffer = function() min_y = Math.max(min_y, 0); max_y = Math.min(max_y, this.svga_height); - this.bus.send("screen-fill-buffer-end", [{ + this.screen.update_buffer([{ image_data: this.image_data, screen_x: 0, screen_y: min_y, buffer_x: 0, buffer_y: min_y, @@ -2470,7 +2449,7 @@ VGAScreen.prototype.screen_fill_buffer = function() { this.vga_replot(); this.vga_redraw(); - this.bus.send("screen-fill-buffer-end", this.layers); + this.screen.update_buffer(this.layers); } this.reset_diffs(); diff --git a/tests/api/floppy-insert-eject.js b/tests/api/floppy-insert-eject.js index 0470948c19..12246c87ac 100755 --- a/tests/api/floppy-insert-eject.js +++ b/tests/api/floppy-insert-eject.js @@ -20,22 +20,19 @@ const emulator = new V86({ disable_jit: +process.env.DISABLE_JIT, }); -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 " }, - { call: () => { - console.log("Passed"); - emulator.stop(); - } - }, -]); +const timeout = setTimeout(() => { + throw new Error("Timeout"); +}, 60 * 1000); + +setTimeout(async () => +{ + await emulator.wait_until_vga_screen_contains("C:\\> "); + emulator.keyboard_send_text("dir A:\n"); + await emulator.wait_until_vga_screen_contains("Abort, Retry, Fail?"); + emulator.keyboard_send_text("F"); + emulator.set_fda({ url: __dirname + "/../../images/freedos722.img" }); + emulator.keyboard_send_text("dir A:\n"); + await emulator.wait_until_vga_screen_contains("FDOS "); + emulator.stop(); + clearTimeout(timeout); +}, 1000); diff --git a/tests/api/serial.js b/tests/api/serial.js index 68ff868ca4..84132b8964 100755 --- a/tests/api/serial.js +++ b/tests/api/serial.js @@ -26,12 +26,12 @@ const emulator = new V86(config); let serial_data = []; -emulator.automatically([ - { sleep: 1 }, - { vga_text: "/root% " }, - { call: () => { console.log("Booted, sending file to ttyS0"); } }, - { keyboard_send: "cat /bin/busybox > /dev/ttyS0\n" }, -]); +setTimeout(async () => +{ + await emulator.wait_until_vga_screen_contains("/root% "); + console.log("Booted, sending file to ttyS0"); + emulator.keyboard_send_text("cat /bin/busybox > /dev/ttyS0\n"); +}, 1000); const timeout = setTimeout(() => { throw new Error("Timeout"); diff --git a/tests/api/state.js b/tests/api/state.js index bfaf859be8..91a6180fe7 100755 --- a/tests/api/state.js +++ b/tests/api/state.js @@ -58,12 +58,6 @@ const config_large_memory = { async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } -function screen_contains(emulator, text) -{ - const lines = emulator.screen_adapter.get_text_screen(); - return lines.some(line => line.startsWith(text)); -} - async function run_test(name, config, done) { const emulator = new V86(config); @@ -78,18 +72,14 @@ async function run_test(name, config, done) console.log("Restoring: %s", name); await emulator.restore_state(state); - do - { - await sleep(1000); - } - while(!screen_contains(emulator, "~% ")); + await emulator.wait_until_vga_screen_contains("~% "); + await sleep(1000); emulator.keyboard_send_text("echo -n test; echo passed\n"); - await sleep(1000); const lines = emulator.screen_adapter.get_text_screen(); - if(!screen_contains(emulator, "testpassed")) + if(!lines.some(line => line.startsWith("testpassed"))) { console.warn("Failed: " + name); console.warn(lines.map(line => line.replace(/\x00/g, " "))); diff --git a/tests/full/run.js b/tests/full/run.js index 72b52fdb4e..ae4a906a2b 100755 --- a/tests/full/run.js +++ b/tests/full/run.js @@ -1201,20 +1201,17 @@ function run_test(test, done) check_test_done(); }); - emulator.add_listener("screen-set-mode", function(is_graphical) + emulator.add_listener("screen-set-size", function(args) { - graphical_test_done = is_graphical; - check_test_done(); - }); + const [w, h, bpp] = args; + graphical_test_done = bpp !== 0; - emulator.add_listener("screen-set-size-graphical", function(size) - { if(test.expect_graphical_size) { - size_test_done = size[0] === test.expect_graphical_size[0] && - size[1] === test.expect_graphical_size[1]; - check_test_done(); + size_test_done = w === test.expect_graphical_size[0] && h === test.expect_graphical_size[1]; } + + check_test_done(); }); emulator.add_listener("screen-put-char", function(chr) From 2ee931ba7b0db7dcccde27cd3dbc0912a64901aa Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 13 Sep 2024 13:34:31 -0600 Subject: [PATCH 30/39] remove IDEInterface.stats (unused and was never publicly documented) --- src/ide.js | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/src/ide.js b/src/ide.js index 1a5d3db451..2469115680 100644 --- a/src/ide.js +++ b/src/ide.js @@ -518,16 +518,6 @@ function IDEInterface(device, cpu, buffer, is_cd, device_nr, interface_nr, bus) // rtc.cmos_read(CMOS_BIOS_DISKTRANSFLAG) | 1 << (nr * 4 + 2)); // slave } - /** @const */ - this.stats = { - sectors_read: 0, - sectors_written: 0, - bytes_read: 0, - bytes_written: 0, - loading: false, - }; - - this.buffer = buffer; /** @type {number} */ @@ -1967,27 +1957,18 @@ IDEInterface.prototype.data_set = function(data) IDEInterface.prototype.report_read_start = function() { - this.stats.loading = true; this.bus.send("ide-read-start"); }; IDEInterface.prototype.report_read_end = function(byte_count) { - this.stats.loading = false; - - var sector_count = byte_count / this.sector_size | 0; - this.stats.sectors_read += sector_count; - this.stats.bytes_read += byte_count; - + const sector_count = byte_count / this.sector_size | 0; this.bus.send("ide-read-end", [this.nr, byte_count, sector_count]); }; IDEInterface.prototype.report_write = function(byte_count) { - var sector_count = byte_count / this.sector_size | 0; - this.stats.sectors_written += sector_count; - this.stats.bytes_written += byte_count; - + const sector_count = byte_count / this.sector_size | 0; this.bus.send("ide-write-end", [this.nr, byte_count, sector_count]); }; From 14479d6b046de71496d267c377bb7cf9c6e105c1 Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 13 Sep 2024 13:37:18 -0600 Subject: [PATCH 31/39] mention OS/2 museum as the source of some OSes --- Readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.md b/Readme.md index 7859752468..3ef101304d 100644 --- a/Readme.md +++ b/Readme.md @@ -207,6 +207,7 @@ repository under their own licenses: - [Berkeley SoftFloat](http://www.jhauser.us/arithmetic/SoftFloat.html) is included to precisely emulate 80-bit floating point numbers - [The jor1k project](https://github.com/s-macke/jor1k) for 9p, filesystem and uart drivers - [WinWorld](https://winworldpc.com/) sources of some old operating systems +- [OS/2 Museum](https://www.os2museum.com/) sources of some old operating systems ## More questions? From a4df314c82822b92f35fb924e62f13a2d294d1d6 Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 13 Sep 2024 13:53:34 -0600 Subject: [PATCH 32/39] add "Exploring The IBM Personal Computer" --- src/browser/main.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/browser/main.js b/src/browser/main.js index a2d6ebcde9..4d63101ac2 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -1216,6 +1216,14 @@ }, homepage: "https://github.com/ybuzoku/MCP", }, + { + id: "ibm-exploring", + name: "Exploring The IBM Personal Computer", + fda: { + url: host + "ibm-exploring.img", + size: 368640, + }, + }, ]; if(DEBUG) From 03a9b89ffb7a344513bf3c2f94b090fa810e6a4a Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 13 Sep 2024 13:57:11 -0600 Subject: [PATCH 33/39] rename start_eip to eip_offset_in_page and consistently mask off upper bits --- src/rust/codegen.rs | 12 +-- src/rust/cpu/cpu.rs | 80 ++++++++++++-------- tests/expect/tests/indirect-call.wast | 2 +- tests/expect/tests/mem32r.wast | 2 +- tests/expect/tests/mem32rmw.wast | 4 +- tests/expect/tests/mem32w.wast | 2 +- tests/expect/tests/mov-immoffs.wast | 2 +- tests/expect/tests/sti.wast | 2 +- tests/expect/tests/task_switch_test.wast | 2 +- tests/expect/tests/task_switch_test_sse.wast | 2 +- 10 files changed, 64 insertions(+), 46 deletions(-) diff --git a/src/rust/codegen.rs b/src/rust/codegen.rs index 05b48a8ff9..aebf46edb1 100644 --- a/src/rust/codegen.rs +++ b/src/rust/codegen.rs @@ -1112,7 +1112,7 @@ pub fn gen_safe_read_write( } ctx.builder - .const_i32(ctx.start_of_current_instruction as i32); + .const_i32(ctx.start_of_current_instruction as i32 & 0xFFF); match bits { BitSize::BYTE => { @@ -1414,7 +1414,7 @@ pub fn gen_task_switch_test(ctx: &mut JitContext) { gen_fn1_const( ctx.builder, "task_switch_test_jit", - ctx.start_of_current_instruction, + ctx.start_of_current_instruction & 0xFFF, ); ctx.builder.br(ctx.exit_with_fault_label); } @@ -1436,7 +1436,7 @@ pub fn gen_task_switch_test_mmx(ctx: &mut JitContext) { gen_fn1_const( ctx.builder, "task_switch_test_mmx_jit", - ctx.start_of_current_instruction, + ctx.start_of_current_instruction & 0xFFF, ); ctx.builder.br(ctx.exit_with_fault_label); } @@ -2507,7 +2507,7 @@ pub fn gen_trigger_de(ctx: &mut JitContext) { gen_fn1_const( ctx.builder, "trigger_de_jit", - ctx.start_of_current_instruction, + ctx.start_of_current_instruction & 0xFFF, ); gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction); ctx.builder.br(ctx.exit_with_fault_label); @@ -2517,7 +2517,7 @@ pub fn gen_trigger_ud(ctx: &mut JitContext) { gen_fn1_const( ctx.builder, "trigger_ud_jit", - ctx.start_of_current_instruction, + ctx.start_of_current_instruction & 0xFFF, ); gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction); ctx.builder.br(ctx.exit_with_fault_label); @@ -2528,7 +2528,7 @@ pub fn gen_trigger_gp(ctx: &mut JitContext, error_code: u32) { ctx.builder, "trigger_gp_jit", error_code, - ctx.start_of_current_instruction, + ctx.start_of_current_instruction & 0xFFF, ); gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction); ctx.builder.br(ctx.exit_with_fault_label); diff --git a/src/rust/cpu/cpu.rs b/src/rust/cpu/cpu.rs index 1bca02d106..36c4c971b9 100644 --- a/src/rust/cpu/cpu.rs +++ b/src/rust/cpu/cpu.rs @@ -2158,30 +2158,34 @@ pub unsafe fn clear_tlb() { } #[no_mangle] -pub unsafe fn trigger_de_jit(start_eip: i32) { +pub unsafe fn trigger_de_jit(eip_offset_in_page: i32) { dbg_log!("#de in jit mode"); - *instruction_pointer = *instruction_pointer & !0xFFF | start_eip & 0xFFF; + dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000); + *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page; jit_fault = Some((CPU_EXCEPTION_DE, None)) } #[no_mangle] -pub unsafe fn trigger_ud_jit(start_eip: i32) { +pub unsafe fn trigger_ud_jit(eip_offset_in_page: i32) { dbg_log!("#ud in jit mode"); - *instruction_pointer = *instruction_pointer & !0xFFF | start_eip & 0xFFF; + dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000); + *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page; jit_fault = Some((CPU_EXCEPTION_UD, None)) } #[no_mangle] -pub unsafe fn trigger_nm_jit(start_eip: i32) { +pub unsafe fn trigger_nm_jit(eip_offset_in_page: i32) { dbg_log!("#nm in jit mode"); - *instruction_pointer = *instruction_pointer & !0xFFF | start_eip & 0xFFF; + dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000); + *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page; jit_fault = Some((CPU_EXCEPTION_NM, None)) } #[no_mangle] -pub unsafe fn trigger_gp_jit(code: i32, start_eip: i32) { +pub unsafe fn trigger_gp_jit(code: i32, eip_offset_in_page: i32) { dbg_log!("#gp in jit mode"); - *instruction_pointer = *instruction_pointer & !0xFFF | start_eip & 0xFFF; + dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000); + *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page; jit_fault = Some((CPU_EXCEPTION_GP, Some(code))) } @@ -3357,13 +3361,19 @@ pub fn report_safe_read_write_jit_slow(address: u32, entry: i32) { struct ScratchBuffer([u8; 0x1000 * 2]); static mut jit_paging_scratch_buffer: ScratchBuffer = ScratchBuffer([0; 2 * 0x1000]); -pub unsafe fn safe_read_slow_jit(addr: i32, bitsize: i32, start_eip: i32, is_write: bool) -> i32 { +pub unsafe fn safe_read_slow_jit( + addr: i32, + bitsize: i32, + eip_offset_in_page: i32, + is_write: bool, +) -> i32 { + dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000); if is_write && Page::page_of(*instruction_pointer as u32) == Page::page_of(addr as u32) { // XXX: Check based on virtual address dbg_log!( "SMC (rmw): bits={} eip={:x} writeaddr={:x}", bitsize, - start_eip as u32, + (*instruction_pointer & !0xFFF | eip_offset_in_page) as u32, addr as u32 ); } @@ -3375,7 +3385,7 @@ pub unsafe fn safe_read_slow_jit(addr: i32, bitsize: i32, start_eip: i32, is_wri translate_address_read_jit(addr) } { Err(()) => { - *instruction_pointer = *instruction_pointer & !0xFFF | start_eip & 0xFFF; + *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page; return 1; }, Ok(addr) => addr, @@ -3389,7 +3399,7 @@ pub unsafe fn safe_read_slow_jit(addr: i32, bitsize: i32, start_eip: i32, is_wri translate_address_read_jit(boundary_addr) } { Err(()) => { - *instruction_pointer = *instruction_pointer & !0xFFF | start_eip & 0xFFF; + *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page; return 1; }, Ok(addr) => addr, @@ -3499,14 +3509,15 @@ pub unsafe fn safe_write_slow_jit( bitsize: i32, value_low: u64, value_high: u64, - start_eip: i32, + eip_offset_in_page: i32, ) -> i32 { + dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000); if Page::page_of(*instruction_pointer as u32) == Page::page_of(addr as u32) { // XXX: Check based on virtual address dbg_log!( "SMC: bits={} eip={:x} writeaddr={:x}", bitsize, - start_eip as u32, + (*instruction_pointer & !0xFFF | eip_offset_in_page) as u32, addr as u32 ); } @@ -3514,7 +3525,7 @@ pub unsafe fn safe_write_slow_jit( let (addr_low, can_skip_dirty_page) = match translate_address_write_jit_and_can_skip_dirty(addr) { Err(()) => { - *instruction_pointer = *instruction_pointer & !0xFFF | start_eip & 0xFFF; + *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page; return 1; }, Ok(x) => x, @@ -3523,7 +3534,7 @@ pub unsafe fn safe_write_slow_jit( let (addr_high, _) = match translate_address_write_jit_and_can_skip_dirty((addr | 0xFFF) + 1) { Err(()) => { - *instruction_pointer = *instruction_pointer & !0xFFF | start_eip & 0xFFF; + *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page; return 1; }, Ok(x) => x, @@ -3584,24 +3595,29 @@ pub unsafe fn safe_write_slow_jit( } #[no_mangle] -pub unsafe fn safe_write8_slow_jit(addr: i32, value: u32, start_eip: i32) -> i32 { - safe_write_slow_jit(addr, 8, value as u64, 0, start_eip) +pub unsafe fn safe_write8_slow_jit(addr: i32, value: u32, eip_offset_in_page: i32) -> i32 { + safe_write_slow_jit(addr, 8, value as u64, 0, eip_offset_in_page) } #[no_mangle] -pub unsafe fn safe_write16_slow_jit(addr: i32, value: u32, start_eip: i32) -> i32 { - safe_write_slow_jit(addr, 16, value as u64, 0, start_eip) +pub unsafe fn safe_write16_slow_jit(addr: i32, value: u32, eip_offset_in_page: i32) -> i32 { + safe_write_slow_jit(addr, 16, value as u64, 0, eip_offset_in_page) } #[no_mangle] -pub unsafe fn safe_write32_slow_jit(addr: i32, value: u32, start_eip: i32) -> i32 { - safe_write_slow_jit(addr, 32, value as u64, 0, start_eip) +pub unsafe fn safe_write32_slow_jit(addr: i32, value: u32, eip_offset_in_page: i32) -> i32 { + safe_write_slow_jit(addr, 32, value as u64, 0, eip_offset_in_page) } #[no_mangle] -pub unsafe fn safe_write64_slow_jit(addr: i32, value: u64, start_eip: i32) -> i32 { - safe_write_slow_jit(addr, 64, value, 0, start_eip) +pub unsafe fn safe_write64_slow_jit(addr: i32, value: u64, eip_offset_in_page: i32) -> i32 { + safe_write_slow_jit(addr, 64, value, 0, eip_offset_in_page) } #[no_mangle] -pub unsafe fn safe_write128_slow_jit(addr: i32, low: u64, high: u64, start_eip: i32) -> i32 { - safe_write_slow_jit(addr, 128, low, high, start_eip) +pub unsafe fn safe_write128_slow_jit( + addr: i32, + low: u64, + high: u64, + eip_offset_in_page: i32, +) -> i32 { + safe_write_slow_jit(addr, 128, low, high, eip_offset_in_page) } pub unsafe fn safe_write8(addr: i32, value: i32) -> OrPageFault<()> { @@ -3904,9 +3920,10 @@ pub unsafe fn set_mxcsr(new_mxcsr: i32) { } #[no_mangle] -pub unsafe fn task_switch_test_jit(start_eip: i32) { +pub unsafe fn task_switch_test_jit(eip_offset_in_page: i32) { dbg_assert!(0 != *cr & (CR0_EM | CR0_TS)); - trigger_nm_jit(start_eip); + dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000); + trigger_nm_jit(eip_offset_in_page); } pub unsafe fn task_switch_test_mmx() -> bool { @@ -3927,15 +3944,16 @@ pub unsafe fn task_switch_test_mmx() -> bool { } #[no_mangle] -pub unsafe fn task_switch_test_mmx_jit(start_eip: i32) { +pub unsafe fn task_switch_test_mmx_jit(eip_offset_in_page: i32) { + dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000); if *cr.offset(4) & CR4_OSFXSR == 0 { dbg_log!("Warning: Unimplemented task switch test with cr4.osfxsr=0"); } if 0 != *cr & CR0_EM { - trigger_ud_jit(start_eip); + trigger_ud_jit(eip_offset_in_page); } else if 0 != *cr & CR0_TS { - trigger_nm_jit(start_eip); + trigger_nm_jit(eip_offset_in_page); } else { dbg_assert!(false); diff --git a/tests/expect/tests/indirect-call.wast b/tests/expect/tests/indirect-call.wast index a402fac447..0f48efdc73 100644 --- a/tests/expect/tests/indirect-call.wast +++ b/tests/expect/tests/indirect-call.wast @@ -150,7 +150,7 @@ (then (call $e.trigger_gp_jit (i32.const 0) - (i32.const 4096)) + (i32.const 0)) (br $B1))) (i32.load (i32.const 748)) diff --git a/tests/expect/tests/mem32r.wast b/tests/expect/tests/mem32r.wast index d3acde7fdb..269d4a4c0b 100644 --- a/tests/expect/tests/mem32r.wast +++ b/tests/expect/tests/mem32r.wast @@ -76,7 +76,7 @@ (then (call $e.trigger_gp_jit (i32.const 0) - (i32.const 4096)) + (i32.const 0)) (br $B1))) (i32.load (i32.const 748)) diff --git a/tests/expect/tests/mem32rmw.wast b/tests/expect/tests/mem32rmw.wast index 4fec926ca9..6cd1f6541e 100644 --- a/tests/expect/tests/mem32rmw.wast +++ b/tests/expect/tests/mem32rmw.wast @@ -78,7 +78,7 @@ (then (call $e.trigger_gp_jit (i32.const 0) - (i32.const 4096)) + (i32.const 0)) (br $B1))) (i32.load (i32.const 748)) @@ -175,7 +175,7 @@ (call $e.safe_write32_slow_jit (get_local $l9) (get_local $l12) - (i32.const 4096)) + (i32.const 0)) (i32.const 1)) (then (call $e.bug_gen_safe_read_write_page_fault diff --git a/tests/expect/tests/mem32w.wast b/tests/expect/tests/mem32w.wast index 5944213664..c5fb3c9675 100644 --- a/tests/expect/tests/mem32w.wast +++ b/tests/expect/tests/mem32w.wast @@ -76,7 +76,7 @@ (then (call $e.trigger_gp_jit (i32.const 0) - (i32.const 4096)) + (i32.const 0)) (br $B1))) (i32.load (i32.const 748)) diff --git a/tests/expect/tests/mov-immoffs.wast b/tests/expect/tests/mov-immoffs.wast index 54ff92e549..35236190fa 100644 --- a/tests/expect/tests/mov-immoffs.wast +++ b/tests/expect/tests/mov-immoffs.wast @@ -74,7 +74,7 @@ (then (call $e.trigger_gp_jit (i32.const 0) - (i32.const 4096)) + (i32.const 0)) (br $B1))) (i32.load (i32.const 748)) diff --git a/tests/expect/tests/sti.wast b/tests/expect/tests/sti.wast index cf3afa0ab4..47f284d150 100644 --- a/tests/expect/tests/sti.wast +++ b/tests/expect/tests/sti.wast @@ -150,7 +150,7 @@ (then (call $e.trigger_gp_jit (i32.const 0) - (i32.const 4096)) + (i32.const 0)) (br $B1))) (i32.store (i32.const 560) diff --git a/tests/expect/tests/task_switch_test.wast b/tests/expect/tests/task_switch_test.wast index 9ab8482a42..d7a174d4c5 100644 --- a/tests/expect/tests/task_switch_test.wast +++ b/tests/expect/tests/task_switch_test.wast @@ -76,7 +76,7 @@ (i32.const 12)) (then (call $e.task_switch_test_jit - (i32.const 4096)) + (i32.const 0)) (br $B1))) (i32.const 1) (call $e.fpu_get_sti_jit diff --git a/tests/expect/tests/task_switch_test_sse.wast b/tests/expect/tests/task_switch_test_sse.wast index 02c869d9b7..a4a1904b15 100644 --- a/tests/expect/tests/task_switch_test_sse.wast +++ b/tests/expect/tests/task_switch_test_sse.wast @@ -74,7 +74,7 @@ (i32.const 12)) (then (call $e.task_switch_test_mmx_jit - (i32.const 4096)) + (i32.const 0)) (br $B1))) (i64.store (i32.const 1136) From db43d68b1dce9ebfa221930714998e80642fccbd Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 13 Sep 2024 15:24:33 -0600 Subject: [PATCH 34/39] add "screen" option --- src/browser/screen.js | 6 ++++-- src/browser/starter.js | 18 ++++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/browser/screen.js b/src/browser/screen.js index 351dc94972..f4b0eb0899 100644 --- a/src/browser/screen.js +++ b/src/browser/screen.js @@ -3,12 +3,14 @@ /** * Adapter to use visual screen in browsers (in contrast to node) * @constructor + * @param {Object} options */ -function ScreenAdapter(screen_container, screen_fill_buffer) +function ScreenAdapter(options, screen_fill_buffer) { + const screen_container = options.container; this.screen_fill_buffer = screen_fill_buffer; - console.assert(screen_container, "1st argument must be a DOM container"); + console.assert(screen_container, "options.container must be provided"); var graphic_screen = screen_container.getElementsByTagName("canvas")[0], diff --git a/src/browser/starter.js b/src/browser/starter.js index 0054fb2cc1..6028127a04 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -45,7 +45,11 @@ * see [serial.html](../examples/serial.html). * * - `screen_container HTMLElement` (No screen) - An HTMLElement. This should - * have a certain structure, see [basic.html](../examples/basic.html). + * have a certain structure, see [basic.html](../examples/basic.html). Only + * provided for backwards compatibility, use `screen` instead. + * + * - `screen Object` (No screen) - An object with the following properties: + * - `container HTMLElement`: An HTMLElement, see above. * * *** * @@ -312,18 +316,24 @@ V86.prototype.continue_init = async function(emulator, options) // TODO: Should be properly fixed in restore_state settings.enable_ne2k = true; + const screen_options = options.screen || {}; + if(options.screen_container) + { + screen_options.container = options.screen_container; + } + if(!options.disable_keyboard) { this.keyboard_adapter = new KeyboardAdapter(this.bus); } if(!options.disable_mouse) { - this.mouse_adapter = new MouseAdapter(this.bus, options.screen_container); + this.mouse_adapter = new MouseAdapter(this.bus, screen_options.container); } - if(options.screen_container) + if(screen_options.container) { - this.screen_adapter = new ScreenAdapter(options.screen_container, () => this.v86.cpu.devices.vga.screen_fill_buffer()); + this.screen_adapter = new ScreenAdapter(screen_options, () => this.v86.cpu.devices.vga.screen_fill_buffer()); } else { From ee36bd4f7488ace79f3c0c397920a723ce8b024f Mon Sep 17 00:00:00 2001 From: Fabian Date: Fri, 13 Sep 2024 15:54:49 -0600 Subject: [PATCH 35/39] add screen.disable_autoscale option --- src/browser/screen.js | 3 ++- src/browser/starter.js | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/browser/screen.js b/src/browser/screen.js index f4b0eb0899..1830086155 100644 --- a/src/browser/screen.js +++ b/src/browser/screen.js @@ -331,7 +331,8 @@ function ScreenAdapter(options, screen_fill_buffer) graphic_screen.height = height; // add some scaling to tiny resolutions - if(width <= 640 && + if(!options.disable_autoscale && + width <= 640 && width * 2 < window.innerWidth * window.devicePixelRatio && height * 2 < window.innerHeight * window.devicePixelRatio) { diff --git a/src/browser/starter.js b/src/browser/starter.js index 6028127a04..562c89866f 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -49,7 +49,8 @@ * provided for backwards compatibility, use `screen` instead. * * - `screen Object` (No screen) - An object with the following properties: - * - `container HTMLElement`: An HTMLElement, see above. + * - `container HTMLElement` - An HTMLElement, see above. + * - `disable_autoscale boolean` (false) - Disable automatic scaling of small resolutions. * * *** * @@ -91,6 +92,9 @@ disable_mouse: (boolean|undefined), disable_keyboard: (boolean|undefined), wasm_fn: (Function|undefined), + screen: ({ + disable_autoscale: (boolean|undefined), + } | undefined), }} options * @constructor */ From 0d93d366292a77011180e912e06f2c0d161b9427 Mon Sep 17 00:00:00 2001 From: Christian Schnell Date: Sun, 25 Aug 2024 07:58:13 +0200 Subject: [PATCH 36/39] bugfix: reinitialize CanvasRenderingContext2D after resizing its HTML canvas element. A drawing context derived from a canvas loses its configuration when its canvas gets resized, added code to reinitialize graphic_context. --- src/browser/screen.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/browser/screen.js b/src/browser/screen.js index 1830086155..df8945139d 100644 --- a/src/browser/screen.js +++ b/src/browser/screen.js @@ -329,6 +329,8 @@ function ScreenAdapter(options, screen_fill_buffer) graphic_screen.width = width; graphic_screen.height = height; + // graphic_context loses its configuration when its graphic_screen gets resized, reinitialize + graphic_context.imageSmoothingEnabled = false; // add some scaling to tiny resolutions if(!options.disable_autoscale && From e1d55a73907eb42683b3061a87b0fbd071fecd9b Mon Sep 17 00:00:00 2001 From: Fabian Date: Sun, 15 Sep 2024 12:50:15 -0600 Subject: [PATCH 37/39] remove references to utf8.js --- debug.html | 2 +- nodejs-loader.mjs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/debug.html b/debug.html index 5dbca6a9cd..9bc0c51953 100644 --- a/debug.html +++ b/debug.html @@ -17,7 +17,7 @@ var LIB_FILES = ""; // jor1k stuff -LIB_FILES += " jor1k.js 9p.js filesystem.js marshall.js utf8.js"; +LIB_FILES += " jor1k.js 9p.js filesystem.js marshall.js"; var BUILD_FILES = "capstone-x86.min.js libwabt.js"; diff --git a/nodejs-loader.mjs b/nodejs-loader.mjs index 3b5e5252e4..ae184c777d 100644 --- a/nodejs-loader.mjs +++ b/nodejs-loader.mjs @@ -50,7 +50,6 @@ let files = [ "lib/filesystem.js", "lib/jor1k.js", "lib/marshall.js", - "lib/utf8.js", "src/browser/screen.js", "src/browser/keyboard.js", From c6258c1abd5f404f0700b310c638b20e918c94a7 Mon Sep 17 00:00:00 2001 From: Fabian Date: Sun, 15 Sep 2024 13:04:38 -0600 Subject: [PATCH 38/39] allow configuring network card type, slightly refactor V86 interface (breaking change) remove network_adapter option for now --- src/browser/main.js | 5 ++++- src/browser/starter.js | 33 ++++++++++++++++++++------------- src/cpu.js | 2 +- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/browser/main.js b/src/browser/main.js index 4d63101ac2..186b0a0a96 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -1698,7 +1698,10 @@ boot_order: settings.boot_order || parseInt($("boot_order").value, 16) || 0, - network_relay_url: networking_proxy, + net_device: { + type: "ne2k", + relay_url: networking_proxy, + }, bios: settings.bios || bios, vga_bios: settings.bios ? null : vga_bios, diff --git a/src/browser/starter.js b/src/browser/starter.js index 562c89866f..e80fb16ccb 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -3,11 +3,11 @@ /** * Constructor for emulator instances. * - * Usage: `var emulator = new V86(options);` + * Usage: `new V86(options);` * * Options can have the following properties (all optional, default in parenthesis): * - * - `memory_size number` (16 * 1024 * 1024) - The memory size in bytes, should + * - `memory_size number` (64 * 1024 * 1024) - The memory size in bytes, should * be a power of 2. * - `vga_memory_size number` (8 * 1024 * 1024) - VGA memory size in bytes. * @@ -19,7 +19,15 @@ * * - `network_relay_url string` (No network card) - The url of a server running * websockproxy. See [networking.md](networking.md). Setting this will - * enable an emulated network card. + * enable an emulated ne2k network card. Only provided for backwards + * compatibility, use `net_device` instead. + * + * - `net_device Object` (null) - An object with the following properties: + * - `relay_url: string` - See above + * - `type: "ne2k" | "virtio"` - the type of the emulated cards + * + * - `net_devices Array` - Like `net_device`, but allows specifying + * more than one network card (up to 4). (currently not implemented) * * - `bios Object` (No bios) - Either a url pointing to a bios or an * ArrayBuffer, see below. @@ -297,28 +305,27 @@ V86.prototype.continue_init = async function(emulator, options) settings.cpuid_level = options.cpuid_level; settings.virtio_console = options.virtio_console; - if(options.network_adapter) + const relay_url = options.network_relay_url || options.net_device && options.net_device.relay_url; + if(relay_url) { - this.network_adapter = options.network_adapter(this.bus); - } - else if(options.network_relay_url) - { - if(options.network_relay_url === "fetch") + // TODO: remove bus, use direct calls instead + if(relay_url === "fetch") { this.network_adapter = new FetchNetworkAdapter(this.bus); } - else if(options.network_relay_url.startsWith("wisp://") || options.network_relay_url.startsWith("wisps://")) { - this.network_adapter = new WispNetworkAdapter(options.network_relay_url, this.bus, options); + else if(relay_url.startsWith("wisp://") || relay_url.startsWith("wisps://")) + { + this.network_adapter = new WispNetworkAdapter(relay_url, this.bus, options); } else { - this.network_adapter = new NetworkAdapter(options.network_relay_url, this.bus); + this.network_adapter = new NetworkAdapter(relay_url, this.bus); } } // Enable unconditionally, so that state images don't miss hardware // TODO: Should be properly fixed in restore_state - settings.enable_ne2k = true; + settings.net_device = options.net_device || { type: "ne2k" }; const screen_options = options.screen || {}; if(options.screen_container) diff --git a/src/cpu.js b/src/cpu.js index 5334af70f0..b10c990743 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -969,7 +969,7 @@ CPU.prototype.init = function(settings, device_bus) this.devices.pit = new PIT(this, device_bus); - if(settings.enable_ne2k) + if(settings.net_device.type === "ne2k") { this.devices.net = new Ne2k(this, device_bus, settings.preserve_mac_from_state_image, settings.mac_address_translation); } From 58d43361499d65e24295b20cb3857f123e261337 Mon Sep 17 00:00:00 2001 From: Rob Blanckaert Date: Sat, 6 Jul 2024 15:12:34 -0700 Subject: [PATCH 39/39] virtio_net device --- Makefile | 5 +- debug.html | 2 +- nodejs-loader.mjs | 2 +- src/browser/starter.js | 1 + src/cpu.js | 14 +- src/virtio.js | 6 +- src/virtio_net.js | 246 +++++++++++++++++++++++++++++++++ tests/devices/fetch_network.js | 24 +++- 8 files changed, 290 insertions(+), 10 deletions(-) create mode 100644 src/virtio_net.js diff --git a/Makefile b/Makefile index f079198c8d..892b1fe07e 100644 --- a/Makefile +++ b/Makefile @@ -81,8 +81,8 @@ CARGO_FLAGS=$(CARGO_FLAGS_SAFE) -C target-feature=+bulk-memory -C target-feature CORE_FILES=const.js config.js io.js main.js lib.js buffer.js ide.js pci.js floppy.js \ memory.js dma.js pit.js vga.js ps2.js rtc.js uart.js \ acpi.js apic.js ioapic.js \ - state.js ne2k.js sb16.js virtio.js virtio_console.js bus.js log.js \ - cpu.js debug.js \ + state.js ne2k.js sb16.js virtio.js virtio_console.js virtio_net.js \ + bus.js log.js cpu.js debug.js \ elf.js kernel.js LIB_FILES=9p.js filesystem.js jor1k.js marshall.js BROWSER_FILES=screen.js keyboard.js mouse.js speaker.js serial.js \ @@ -306,6 +306,7 @@ devices-test: all-debug ./tests/devices/virtio_9p.js ./tests/devices/virtio_console.js ./tests/devices/fetch_network.js + USE_VIRTIO=1 ./tests/devices/fetch_network.js ./tests/devices/wisp_network.js rust-test: $(RUST_FILES) diff --git a/debug.html b/debug.html index 9bc0c51953..c2054197ec 100644 --- a/debug.html +++ b/debug.html @@ -11,7 +11,7 @@ "const.js config.js log.js lib.js buffer.js cpu.js debug.js " + "io.js main.js ide.js pci.js floppy.js " + "memory.js dma.js pit.js vga.js ps2.js rtc.js uart.js acpi.js apic.js ioapic.js sb16.js " + - "ne2k.js state.js virtio.js virtio_console.js bus.js elf.js kernel.js"; + "ne2k.js state.js virtio.js virtio_console.js virtio_net.js bus.js elf.js kernel.js"; var BROWSER_FILES = "main.js screen.js keyboard.js mouse.js speaker.js serial.js network.js fake_network.js fetch_network.js wisp_network.js starter.js worker_bus.js print_stats.js filestorage.js"; var LIB_FILES = ""; diff --git a/nodejs-loader.mjs b/nodejs-loader.mjs index ae184c777d..e4ddc465e2 100644 --- a/nodejs-loader.mjs +++ b/nodejs-loader.mjs @@ -38,7 +38,7 @@ let files = [ "src/sb16.js", "src/virtio.js", "src/virtio_console.js", - //"src/virtio_net.js", + "src/virtio_net.js", //"src/virtio_balloon.js", "src/bus.js", diff --git a/src/browser/starter.js b/src/browser/starter.js index e80fb16ccb..154ae3e4f4 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -304,6 +304,7 @@ V86.prototype.continue_init = async function(emulator, options) settings.mac_address_translation = options.mac_address_translation; settings.cpuid_level = options.cpuid_level; settings.virtio_console = options.virtio_console; + settings.virtio_net = options.virtio_net; const relay_url = options.network_relay_url || options.net_device && options.net_device.relay_url; if(relay_url) diff --git a/src/cpu.js b/src/cpu.js index b10c990743..c81759fd42 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -418,6 +418,7 @@ CPU.prototype.get_state = function() state[80] = this.devices.uart2; state[81] = this.devices.uart3; state[82] = this.devices.virtio_console; + state[83] = this.devices.virtio_net; return state; }; @@ -549,6 +550,7 @@ CPU.prototype.set_state = function(state) this.devices.uart2 && this.devices.uart2.set_state(state[80]); this.devices.uart3 && this.devices.uart3.set_state(state[81]); this.devices.virtio_console && this.devices.virtio_console.set_state(state[82]); + this.devices.virtio_net && this.devices.virtio_net.set_state(state[83]); this.fw_value = state[62]; @@ -687,11 +689,15 @@ CPU.prototype.reboot_internal = function() if(this.devices.virtio_9p) { - this.devices.virtio_9p.reset(); + this.devices.virtio_9p.Reset(); } if(this.devices.virtio_console) { - this.devices.virtio_console.reset(); + this.devices.virtio_console.Reset(); + } + if(this.devices.virtio_net) + { + this.devices.virtio_net.Reset(); } this.load_bios(); @@ -973,6 +979,10 @@ CPU.prototype.init = function(settings, device_bus) { this.devices.net = new Ne2k(this, device_bus, settings.preserve_mac_from_state_image, settings.mac_address_translation); } + else if(settings.net_device.type === "virtio") + { + this.devices.virtio_net = new VirtioNet(this, device_bus, settings.preserve_mac_from_state_image); + } if(settings.fs9p) { diff --git a/src/virtio.js b/src/virtio.js index bee2af49ba..7c3f11d5d5 100644 --- a/src/virtio.js +++ b/src/virtio.js @@ -553,7 +553,7 @@ VirtIO.prototype.create_common_capability = function(options) read: () => 0, write: data => { - dbg_log("Warning: High dword of 64 bit queue_desc ignored", LOG_VIRTIO); + if(data !== 0) dbg_log("Warning: High dword of 64 bit queue_desc ignored:" + data, LOG_VIRTIO); }, }, { @@ -571,7 +571,7 @@ VirtIO.prototype.create_common_capability = function(options) read: () => 0, write: data => { - dbg_log("Warning: High dword of 64 bit queue_avail ignored", LOG_VIRTIO); + if(data !== 0) dbg_log("Warning: High dword of 64 bit queue_avail ignored:" + data, LOG_VIRTIO); }, }, { @@ -589,7 +589,7 @@ VirtIO.prototype.create_common_capability = function(options) read: () => 0, write: data => { - dbg_log("Warning: High dword of 64 bit queue_used ignored", LOG_VIRTIO); + if(data !== 0) dbg_log("Warning: High dword of 64 bit queue_used ignored:" + data, LOG_VIRTIO); }, }, ], diff --git a/src/virtio_net.js b/src/virtio_net.js new file mode 100644 index 0000000000..b8e5a4f79c --- /dev/null +++ b/src/virtio_net.js @@ -0,0 +1,246 @@ +"use strict"; + +// https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-2900003 + + +const VIRTIO_NET_F_MAC = 5; +const VIRTIO_NET_F_CTRL_VQ = 17; +const VIRTIO_NET_F_STATUS = 16; +const VIRTIO_NET_F_MQ = 22; +const VIRTIO_NET_F_CTRL_MAC_ADDR = 23; +const VIRTIO_NET_F_MTU = 3; + +const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET = 0; +const VIRTIO_NET_CTRL_MAC_ADDR_SET = 1; + +/** + * @constructor + * @param {CPU} cpu + * @param {BusConnector} bus + * @param {Boolean} preserve_mac_from_state_image + */ +function VirtioNet(cpu, bus, preserve_mac_from_state_image) +{ + /** @const @type {BusConnector} */ + this.bus = bus; + this.id = cpu.devices.net ? 1 : 0; + this.pairs = 1; + this.status = 1; + this.preserve_mac_from_state_image = preserve_mac_from_state_image; + this.mac = new Uint8Array([ + 0x00, 0x22, 0x15, + Math.random() * 255 | 0, + Math.random() * 255 | 0, + Math.random() * 255 | 0, + ]); + + this.bus.send("net" + this.id + "-mac", format_mac(this.mac)); + + const queues = []; + + for(let i = 0; i < this.pairs; ++i) + { + queues.push({size_supported: 32, notify_offset: 0}); + queues.push({size_supported: 32, notify_offset: 1}); + } + queues.push({ + size_supported: 16, + notify_offset: 2, + }); + + /** @type {VirtIO} */ + this.virtio = new VirtIO(cpu, + { + name: "virtio-net", + pci_id: 0x0A << 3, + device_id: 0x1041, + subsystem_device_id: 1, + common: + { + initial_port: 0xC800, + queues: queues, + features: + [ + VIRTIO_NET_F_MAC, + VIRTIO_NET_F_STATUS, + VIRTIO_NET_F_MQ, + VIRTIO_NET_F_MTU, + VIRTIO_NET_F_CTRL_VQ, + VIRTIO_NET_F_CTRL_MAC_ADDR, + VIRTIO_F_VERSION_1, + ], + on_driver_ok: () => {}, + }, + notification: + { + initial_port: 0xC900, + single_handler: false, + handlers: + [ + (queue_id) => + { + // TODO: Full buffer looks like an empty buffer so prevent it from filling + // The kernel gives us a prefilled one, so throw the first bufchain so + // it doesnt look filled. + + const queue = this.virtio.queues[queue_id]; + + const desc_idx = queue.avail_get_entry(queue.avail_last_idx); + const bufchain = new VirtQueueBufferChain(queue, desc_idx); + queue.avail_last_idx = queue.avail_last_idx + 1 & queue.mask; + this.virtio.queues[0].push_reply(bufchain); + this.virtio.queues[0].flush_replies(); + }, + (queue_id) => + { + const queue = this.virtio.queues[queue_id]; + + while(queue.has_request()) + { + const bufchain = queue.pop_request(); + const buffer = new Uint8Array(bufchain.length_readable); + bufchain.get_next_blob(buffer); + this.bus.send("net" + this.id + "-send", buffer.subarray(12)); + this.virtio.queues[queue_id].push_reply(bufchain); + } + this.virtio.queues[queue_id].flush_replies(); + }, + (queue_id) => + { + if(queue_id !== this.pairs * 2) + { + dbg_assert(false, "VirtioConsole Notified for wrong queue: " + queue_id + + " (expected queue_id of 3)"); + return; + } + const queue = this.virtio.queues[queue_id]; + + while(queue.has_request()) + { + const bufchain = queue.pop_request(); + const buffer = new Uint8Array(bufchain.length_readable); + bufchain.get_next_blob(buffer); + + + const parts = marshall.Unmarshall(["b", "b"], buffer, { offset : 0 }); + const xclass = parts[0]; + const command = parts[1]; + + + //this.Ack(queue_id, bufchain); + + switch(xclass << 8 | command) { + case 4 << 8 | VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET: + const data = marshall.Unmarshall(["h"], buffer, { offset : 2 }); + dbg_assert(data[0] === 1); + this.Send(queue_id, bufchain, new Uint8Array([0])); + break; + case 1 << 8 | VIRTIO_NET_CTRL_MAC_ADDR_SET: + this.mac = buffer.subarray(2, 8); + this.Send(queue_id, bufchain, new Uint8Array([0])); + this.bus.send("net" + this.id + "-mac", format_mac(this.mac)); + break; + default: + dbg_assert(false," VirtioConsole received unknown command: " + xclass + ":" + command); + this.Send(queue_id, bufchain, new Uint8Array([1])); + return; + + } + } + }, + ], + }, + isr_status: + { + initial_port: 0xC700, + }, + device_specific: + { + initial_port: 0xC600, + struct: + [0,1,2,3,4,5].map((v,k) => ({ + bytes: 1, + name: "mac_" + k, + read: () => this.mac[k], + write: data => { /* read only */ }, + })).concat( + [ + { + bytes: 2, + name: "status", + read: () => this.status, + write: data => { /* read only */ }, + }, + { + bytes: 2, + name: "max_pairs", + read: () => this.pairs, + write: data => { /* read only */ }, + }, + { + bytes: 2, + name: "mtu", + read: () => 1500, + write: data => {}, + } + ]) + }, + }); + + this.bus.register("net" + this.id + "-receive", data => { + const with_header = new Uint8Array(12 + data.byteLength); + const view = new DataView(with_header.buffer, with_header.byteOffset, with_header.byteLength); + view.setInt16(10, 1); + with_header.set(data, 12); + + const queue = this.virtio.queues[0]; + if(queue.has_request()) { + const bufchain = queue.pop_request(); + bufchain.set_next_blob(with_header); + this.virtio.queues[0].push_reply(bufchain); + this.virtio.queues[0].flush_replies(); + } else { + console.log("No buffer to write into!"); + } + }, this); + +} + + +VirtioNet.prototype.get_state = function() +{ + const state = []; + state[0] = this.virtio; + state[1] = this.id; + + if(this.preserve_mac_from_state_image) + { + this.mac = state[2]; + this.bus.send("net" + this.id + "-mac", format_mac(this.mac)); + } + + return state; +}; + +VirtioNet.prototype.set_state = function(state) +{ + this.virtio.set_state(state[0]); +}; + +VirtioNet.prototype.Reset = function() { + +}; + +VirtioNet.prototype.Send = function (queue_id, bufchain, blob) +{ + bufchain.set_next_blob(blob); + this.virtio.queues[queue_id].push_reply(bufchain); + this.virtio.queues[queue_id].flush_replies(); +}; + +VirtioNet.prototype.Ack = function (queue_id, bufchain) +{ + //bufchain.set_next_blob(new Uint8Array(0)); + this.virtio.queues[queue_id].push_reply(bufchain); + this.virtio.queues[queue_id].flush_replies(); +}; diff --git a/tests/devices/fetch_network.js b/tests/devices/fetch_network.js index c3f7cf8575..8f98bb78ca 100755 --- a/tests/devices/fetch_network.js +++ b/tests/devices/fetch_network.js @@ -4,6 +4,7 @@ process.on("unhandledRejection", exn => { throw exn; }); const TEST_RELEASE_BUILD = +process.env.TEST_RELEASE_BUILD; +const USE_VIRTIO = !!process.env.USE_VIRTIO; const V86 = require(`../../build/${TEST_RELEASE_BUILD ? "libv86" : "libv86-debug"}.js`).V86; @@ -25,6 +26,24 @@ const tests = assert(/lease of 192.168.86.100 obtained/.test(capture), "lease of 192.168.86.100 obtained"); }, }, + { + name: "lspci", + timeout: 60, + start: () => + { + emulator.serial0_send("lspci -k\n"); + emulator.serial0_send("echo -e done\\\\tlspci\n"); + }, + end_trigger: "done\tlspci", + end: (capture) => + { + if(!USE_VIRTIO) { + assert(/ne2k/.test(capture), "ne2k missing from lspci"); + } else { + assert(!/ne2k/.test(capture), "ne2k in lspci"); + } + }, + }, { name: "ifconfig", start: () => @@ -115,7 +134,10 @@ const emulator = new V86({ autostart: true, memory_size: 64 * 1024 * 1024, disable_jit: +process.env.DISABLE_JIT, - network_relay_url: "fetch", + net_device: { + relay_url: "fetch", + type: USE_VIRTIO ? "virtio" : "ne2k", + }, log_level: SHOW_LOGS ? 0x400000 : 0, });

@@ -107,6 +102,10 @@

Setup



Disk images are not uploaded to the server