Skip to content

Commit

Permalink
preliminary v86 support
Browse files Browse the repository at this point in the history
  • Loading branch information
asiekierka committed Oct 30, 2022
1 parent d6547c5 commit 4fd4b5f
Show file tree
Hide file tree
Showing 2 changed files with 267 additions and 0 deletions.
75 changes: 75 additions & 0 deletions example_v86.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<!--
The Emularity: An Example Computer Loader
For use with The Emularity, downloadable at http://www.emularity.com/
SIMPLE STEPS for trying an emulated computer (FreeDOS).
* Check out this repository in your browser-accessible directory;
this file as well as es6-promise.js, browserfs.min.js and loader.js
are required. The logo and images directories are optional, but the
splash screen looks quite a lot better when they're available.
* Clone: https://github.com/asiekierka/v86/tree/emularity
and run "make all".
* Copy bios/seabios.bin, bios/vgabios.bin, build/libv86.js,
build/v86.wasm to "emulators/v86".
* Optionally, acquire an 8x14 EGA font in WOFF format from:
https://int10h.org/oldschool-pc-fonts/fontlist/ and copy it to
"emulators/v86/ega437.woff".
* Download FreeDOS from: https://k.copy.sh/freedos722.img
* Place disk image in an "examples" subdirectory.
* Visit your example_v86.html file with a modern
Javascript-capable browser.
-->

<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>example computer program</title>
<style type="text/css">
@font-face {
font-family: 'Ega437';
src: url('emulators/v86/ega437.woff') format('woff');
}
.emularity-v86-screen-text {
font-family: 'Ega437', monospace !important;
}
</style>
</head>

<body>
<canvas id="canvas" style="width: 50%; height: 50%; display: block; margin: 0 auto;" />
<script type="text/javascript" src="es6-promise.js"></script>
<script type="text/javascript" src="browserfs.min.js"></script>
<script type="text/javascript" src="loader.js"></script>
<script type="text/javascript">
var emulator = new Emulator(document.querySelector("#canvas"),
null,
new V86Loader(V86Loader.nativeResolution(800, 600),
V86Loader.scale(1),
V86Loader.emulatorJS("emulators/v86/libv86.js"),
V86Loader.emulatorWASM("emulators/v86/v86.wasm"),
V86Loader.mountFile("seabios.bin",
V86Loader.fetchFile("BIOS",
"emulators/v86/seabios.bin")),
V86Loader.mountFile("vgabios.bin",
V86Loader.fetchFile("VGA BIOS",
"emulators/v86/vgabios.bin")),
V86Loader.mountFile("freedos.img",
V86Loader.fetchFile("FreeDOS",
"examples/freedos722.img")),
V86Loader.bios("seabios.bin"),
V86Loader.vgaBios("vgabios.bin"),
V86Loader.fda("freedos.img"),
));
emulator.start({ waitAfterDownloading: true });
</script>
</body>

</html>
192 changes: 192 additions & 0 deletions loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -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"),
};
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 4fd4b5f

Please sign in to comment.