diff --git a/example_v86.html b/example_v86.html
new file mode 100644
index 00000000..75ba0c28
--- /dev/null
+++ b/example_v86.html
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+ example computer program
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/loader.js b/loader.js
index 1e304381..5acde595 100644
--- a/loader.js
+++ b/loader.js
@@ -64,6 +64,7 @@ var Module = null;
xmil: img("/images/xmillenium_logo.jpg"),
vmac: img("/images/vmac.png"),
ruffle: img("/images/ruffle.png"),
+ v86: img("/images/v86.png"),
};
} else {
images = { ia: img("other_logos/ia-logo-150x150.png"),
@@ -77,6 +78,7 @@ var Module = null;
xmil: img("other_logos/xmillenium_logo.jpg"),
vmac: img("other_logos/vmac.png"),
ruffle: img("/other_logos/ruffle.png"),
+ v86: img("other_logos/v86.png"),
};
}
@@ -188,6 +190,11 @@ var Module = null;
cfgr = NP2Loader;
get_files = get_vmac_files;
}
+ else if (module && module.indexOf("v86") === 0) {
+ emulator_logo = images.v86;
+ cfgr = V86Loader;
+ get_files = get_v86_files;
+ }
else if (module) {
emulator_logo = images.mame;
cfgr = MAMELoader;
@@ -605,6 +612,49 @@ var Module = null;
return files;
}
+ function get_v86_files(cfgr, metadata, modulecfg, filelist) {
+ var files = [];
+
+ if (modulecfg['bios_filename']) {
+ files.push(cfgr.mountFile('/' + modulecfg['bios_filename'], cfgr.fetchFile("BIOS File", get_bios_url(modulecfg['bios_filename']))));
+ files.push(cfgr.bios(modulecfg['bios_filename']));
+ }
+ if (modulecfg['vga_bios_filename']) {
+ files.push(cfgr.mountFile('/' + modulecfg['vga_bios_filename'], cfgr.fetchFile("VGA BIOS File", get_bios_url(modulecfg['vga_bios_filename']))));
+ files.push(cfgr.vgaBios(modulecfg['vga_bios_filename']));
+ }
+
+ var meta = dict_from_xml(metadata),
+ game_files_counter = {};
+ files_with_ext_from_filelist(filelist, meta.emulator_ext).forEach(function (file, i) {
+ if (modulecfg.peripherals && modulecfg.peripherals[i]) {
+ game_files_counter[file.name] = modulecfg.peripherals[i];
+ }
+ });
+ meta_props_matching(meta, /^v86_drive_([a-zA-Z0-9]+)$/).forEach(function (result) {
+ var key = result[0], periph = result[1][1];
+ game_files_counter[meta[key]] = periph;
+ });
+
+ var game_files = Object.keys(game_files_counter),
+ len = game_files.length;
+ game_files.forEach(function (filename, i) {
+ var title = "Game File ("+ (i+1) +" of "+ len +")",
+ ext = filename.match(/\.([^.]*)$/)[1],
+ url = (filename.includes("/")) ? get_zip_url(filename)
+ : get_zip_url(filename, get_item_name(game)),
+ periph = game_files_counter[filename],
+ path = '/' + periph + '.' + ext,
+ periph_cfg = {};
+ periph_cfg[periph] = {"path": path};
+ files.push(cfgr.mountFile(path,
+ cfgr.fetchFile(title, url)));
+ files.push(periph_cfg);
+ });
+
+ return files;
+ }
+
var get_item_name = function (game_path) {
return game_path.split('/').shift();
};
@@ -949,6 +999,52 @@ var Module = null;
return { extra_np2_args: args };
};
+ /**
+ * V86Loader
+ */
+ function V86Loader() {
+ var config = Array.prototype.reduce.call(arguments, extend);
+ config.runner = V86Runner;
+ return config;
+ }
+ V86Loader.__proto__ = BaseLoader;
+
+ V86Loader.bios = function (filename) {
+ return {"bios": {"path": filename}}
+ };
+
+ V86Loader.vgaBios = function (filename) {
+ return {"vga_bios": {"path": filename}}
+ };
+
+ V86Loader.fda = function (filename) {
+ return {"fda": {"path": filename}}
+ };
+
+ V86Loader.fdb = function (filename) {
+ return {"fdb": {"path": filename}}
+ };
+
+ V86Loader.hda = function (filename) {
+ return {"hda": {"path": filename}}
+ };
+
+ V86Loader.hdb = function (filename) {
+ return {"hda": {"path": filename}}
+ };
+
+ V86Loader.cdrom = function (filename) {
+ return {"cdrom": {"path": filename}}
+ };
+
+ V86Loader.memorySize = function (amount) {
+ return {"memory_size": amount};
+ };
+
+ V86Loader.vgaMemorySize = function (amount) {
+ return {"vga_memory_size": amount};
+ };
+
var build_mame_arguments = function (muted, driver, native_resolution, sample_rate, peripheral, autoboot, extra_args, keepaspect, scale) {
scale = scale || 1;
var args = [driver,
@@ -1276,6 +1372,101 @@ var Module = null;
getfullscreenenabler().call(this._canvas);
};
+ /*
+ * V86Runner
+ */
+ function V86Runner(canvas, game_data) {
+ // v86 needs a specific DOM structure instead of a canvas
+ var screenContainerOuterElt = document.createElement("div");
+ screenContainerOuterElt.id = canvas.id;
+ screenContainerOuterElt.classList = canvas.classList;
+ screenContainerOuterElt.style = canvas.style;
+
+ var screenContainerInnerElt = document.createElement("div");
+ screenContainerInnerElt.classList = ["emularity-v86-screen-container"];
+ screenContainerInnerElt.style = "display:flex;justify-content:center;align-items:center;background-color:#000;";
+
+ var textDivElt = document.createElement("div");
+ textDivElt.classList = ["emularity-v86-screen-text"];
+ textDivElt.style = "font-size:14px;font-family:monospace;line-height:14px;white-space:pre;";
+ var canvasElt = document.createElement("canvas");
+ canvasElt.classList = ["emularity-v86-screen-canvas"];
+ canvasElt.style = "display:none;";
+
+ screenContainerInnerElt.appendChild(textDivElt);
+ screenContainerInnerElt.appendChild(canvasElt);
+ screenContainerOuterElt.appendChild(screenContainerInnerElt);
+ canvas.parentNode.replaceChild(screenContainerOuterElt, canvas);
+
+ var cfg = {};
+ cfg.screen_container = screenContainerInnerElt;
+ cfg.memory_size = game_data.memory_size || 32 << 20;
+ cfg.vga_memory_size = game_data.vga_memory_size || 2 << 20;
+ cfg.autostart = true; // FIXME
+
+ cfg.wasm_fn = env => {
+ return new Promise(async resolve => {
+ const wasm = await WebAssembly.instantiate(game_data.wasmBinary, env);
+ resolve(wasm.instance.exports);
+ });
+ };
+ ["bios", "vga_bios", "fda", "fdb", "cdrom", "hda", "hdb"].forEach(key => {
+ if (game_data[key] && game_data[key]["path"]) {
+ cfg[key] = {"buffer": game_data.fs.readFileSync('/'+game_data[key]["path"], null, flag_r).buffer};
+ }
+ });
+
+ var emu = new V86Starter(cfg);
+ this._emulator = emu;
+ this.ready = null;
+
+ if (game_data["scale"]) {
+ emu.screen_set_scale(game_data["scale"], game_data["scale"]);
+ }
+
+ screenContainerInnerElt.addEventListener('click', function (e) {
+ emu.lock_mouse();
+ });
+ }
+
+ V86Runner.prototype.start = function () {
+ // this._emulator.run(); // FIXME
+ };
+
+ V86Runner.prototype.pause = function () {
+ this._emulator.stop();
+ };
+
+ V86Runner.prototype.stop = function () {
+ this._emulator.stop();
+ };
+
+ V86Runner.prototype.mute = function () {
+ if (this._emulator.is_muted) {
+ this._emulator.speaker_adapter.mixer.set_volume(1, undefined);
+ this._emulator.is_muted = false;
+ }
+ };
+
+ V86Runner.prototype.unmute = function () {
+ if (!this._emulator.is_muted) {
+ this._emulator.speaker_adapter.mixer.set_volume(0, undefined);
+ this._emulator.is_muted = true;
+ }
+ };
+
+ V86Runner.prototype.onStarted = function (func) {
+ this._emulator.add_listener("emulator-started", func);
+ };
+
+ V86Runner.prototype.onReset = function (func) {
+ // not supported
+ };
+
+ V86Runner.prototype.requestFullScreen = function () {
+ getfullscreenenabler().call(this._canvas);
+ };
+
/*
* RuffleRunner
*/
@@ -2202,6 +2393,7 @@ var Module = null;
window.PCELoader = PCELoader;
window.VICELoader = VICELoader;
window.NP2Loader = NP2Loader;
+ window.V86Loader = V86Loader;
window.RuffleLoader = RuffleLoader;
window.Emulator = Emulator;
window._SDL_CreateRGBSurfaceFrom = _SDL_CreateRGBSurfaceFrom;