New publicly usable interface called V86Starter, refactor browser/main.js using it

This commit is contained in:
copy 2015-01-09 04:49:44 +01:00
parent ac7ca3cb4c
commit 1032ef3ef2
7 changed files with 781 additions and 504 deletions

View file

@ -19,7 +19,8 @@ build/cpu.js: src/*.macro.js
# Used for nodejs builds and in order to profile code.
# `debug` gives identifiers a readable name, make sure it doesn't have any side effects.
CLOSURE_READABLE=--formatting PRETTY_PRINT --debug
#CLOSURE_READABLE=--formatting PRETTY_PRINT --debug
CLOSURE_READABLE=--formatting PRETTY_PRINT
CLOSURE_SOURCE_MAP=\
--source_map_format V3\
@ -39,9 +40,9 @@ CORE_FILES=const.js io.js main.js lib.js fpu.js ide.js pci.js floppy.js memory.j
dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js hpet.js acpi.js\
state.js ne2k.js virtio.js bus.js log.js
LIB_FILES=../lib/9p.js ../lib/filesystem.js ../lib/jor1k.js ../lib/marshall.js ../lib/utf8.js
BROWSER_FILES=browser/main.js browser/screen.js\
BROWSER_FILES=browser/screen.js\
browser/keyboard.js browser/mouse.js browser/serial.js\
browser/network.js browser/lib.js
browser/network.js browser/lib.js browser/starter.js
build/v86_all.js: src/*.js src/browser/*.js build/cpu.js lib/*.js
-ls -lh build/v86_all.js
@ -57,21 +58,26 @@ build/v86_all.js: src/*.js src/browser/*.js build/cpu.js lib/*.js
--js $(CORE_FILES)\
--js $(LIB_FILES)\
--js $(BROWSER_FILES)\
--js ../build/cpu.js
--js ../build/cpu.js\
--js browser/main.js
echo "//# sourceMappingURL=v86_all.js.map" >> build/v86_all.js
ls -lh build/v86_all.js
build/libv86.js: src/*.js build/cpu.js lib/*.js
build/libv86.js: src/*.js build/cpu.js lib/*.js src/browser/*.js
cd src &&\
java -jar $(CLOSURE) \
--js_output_file "../build/libv86.js"\
--define=DEBUG=false\
--externs adapter-externs.js\
--define=IN_NODE=false\
--define=IN_BROWSER=true\
--define=IN_WORKER=false\
$(CLOSURE_FLAGS)\
$(CLOSURE_READABLE)\
--output_wrapper ';(function(){%output%})();'\
--js $(CORE_FILES)\
--js $(BROWSER_FILES)\
--js $(LIB_FILES)\
--js ../build/cpu.js

View file

@ -1,96 +1,25 @@
<!doctype html>
<title>Basic Emulator</title><!-- not BASIC! -->
<!-- defines ScreenAdapter -->
<script src="../../src/browser/screen.js"></script>
<!-- defines KeyboardAdapter -->
<script src="../../src/browser/keyboard.js"></script>
<!-- defines v86, SyncBuffer, Bus -->
<script src="../../build/libv86.js"></script>
<script>
// Load a file using XHR as an ArrayBuffer.
// If you want to use this, add some error handling
function load_file(filename, done)
{
var http = new XMLHttpRequest();
http.open("get", filename, true);
http.responseType = "arraybuffer";
http.onload = function(e) {
if(http.readyState === 4 && http.response) {
done(http.response);
}
};
http.send(null);
}
window.onload = function()
{
// For a minimal boot, we need at least 2 images: A bios and a disk image
// (CD, HD or floppy). For non-serial output, a vgabios has to be specified
var images = {};
load_file("../../bios/seabios.bin", function(buffer) {
images.seabios = buffer;
cont(images);
var emulator = new V86Starter({
memory_size: 32 * 1024 * 1024,
vga_memory_size: 2 * 1024 * 1024,
screen_container: document.getElementById("screen_container"),
bios: {
url: "../../bios/seabios.bin",
},
vga_bios: {
url: "../../bios/vgabios.bin",
},
cdrom: {
url: "../../images/linux.iso",
},
autostart: true,
});
load_file("../../bios/vgabios.bin", function(buffer) {
images.vga_bios = buffer;
cont(images);
});
load_file("../../images/linux.iso", function(buffer) {
images.cdrom = buffer;
cont(images);
});
};
function cont(images)
{
if(!images.seabios || !images.vga_bios || !images.cdrom) {
return;
}
var bus = Bus.create();
var container = document.getElementById("screen_container");
var cpu = new v86(bus[0]);
// Adapters implement the communication from or to the emulator. These
// default adapters (defined in browser/*.js) implement what you see
// on copy.sh/v86. You could change them to programatically control the emulator
var keyboard = new KeyboardAdapter(bus[1]);
var screen = new ScreenAdapter(container, bus[1]);
cpu.init({
// load_devices has to be set to true, otherwise no OS can run
load_devices: true,
// The CD image. All disk images have to be wrapped in SyncBuffer or an
// object, that exports the same interface. A few examples are available
// in browser/lib.js
cdrom: new SyncBuffer(images.cdrom),
//hda: images.hd, // a hard disk image
//fda: images.floppy, // a floppy disk image
// The bioses. If you don't need the vgabios, just leave it out.
// Only pass ArrayBuffers here
bios: images.seabios,
vga_bios: images.vga_bios,
vga_memory_size: 2 * 1024 * 1024, // default 8M
memory_size: 32 * 1024 * 1024, // default 64M
});
cpu.run();
}
</script>

View file

@ -5,7 +5,7 @@
var CORE_FILES = "const.js io.js main.js lib.js ide.js fpu.js pci.js floppy.js " +
"memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js acpi.js hpet.js " +
"ne2k.js state.js virtio.js bus.js log.js";
var BROWSER_FILES = "main.js screen.js keyboard.js mouse.js serial.js lib.js network.js";
var BROWSER_FILES = "main.js screen.js keyboard.js mouse.js serial.js lib.js network.js starter.js";
var LIB_FILES = "esprima.js walk.js";
// jor1k stuff

View file

@ -2,38 +2,6 @@
(function()
{
// based on https://github.com/Raynos/after
// All the flow control you'll ever need
function after(count, callback)
{
proxy.count = count;
var result = {};
return (count === 0) ? callback() : proxy;
function proxy(data) {
if(proxy.count <= 0)
{
throw new Error("after called too many times");
}
proxy.count--;
if(data)
{
var keys = Object.keys(data);
for(var i = 0; i < keys.length; i++)
{
result[keys[i]] = data[keys[i]];
}
}
if(proxy.count === 0)
{
callback(result);
}
}
}
function dump_file(ab, name)
{
var blob = new Blob([ab]);
@ -95,18 +63,6 @@
}
}
function lock_mouse(elem)
{
var fn = elem["requestPointerLock"] ||
elem["mozRequestPointerLock"] ||
elem["webkitRequestPointerLock"];
if(fn)
{
fn.call(elem);
}
}
function chr_repeat(chr, count)
{
var result = "";
@ -119,28 +75,37 @@
return result;
}
function show_progress(info, e)
var progress_ticks = 0;
function show_progress(message, e)
{
var el = $("loading");
el.style.display = "block";
if(e.lengthComputable || (info.total && typeof e.loaded === "number"))
{
var per100 = e.loaded / (e.total || info.total) * 100 | 0;
var line = message + " ";
if(typeof e.file_index === "number" && e.file_count)
{
line += "[" + (e.file_index + 1) + "/" + e.file_count + "] ";
}
if(e.total && typeof e.loaded === "number")
{
var per100 = Math.floor(e.loaded / e.total * 100);
per100 = Math.min(100, Math.max(0, per100));
el.textContent = info.msg + " " + per100 + "% [" +
chr_repeat("#", per100 >> 1) +
chr_repeat(" ", 50 - (per100 >> 1)) + "]";
var per50 = Math.floor(per100 / 2);
line += per100 + "% [";
line += chr_repeat("#", per50);
line += chr_repeat(" ", 50 - per50) + "]";
}
else
{
if(!info.ticks)
info.ticks = 0;
el.textContent = info.msg + " " + chr_repeat(".", info.ticks++ % 50);
line += chr_repeat(".", progress_ticks++ % 50);
}
el.textContent = line;
}
function $(id)
@ -163,137 +128,47 @@
return;
}
var settings = {
load_devices: true
};
function load_local(file, type, cb)
{
set_title(file.name);
// SyncFileBuffer:
// - loads the whole disk image into memory, impossible for large files (more than 1GB)
// - can later serve get/set operations fast and synchronously
// - takes some time for first load, neglectable for small files (up to 100Mb)
//
// AsyncFileBuffer:
// - loads slices of the file asynchronously as requested
// - slower get/set
// Heuristics: If file is smaller than 64M, use SyncFileBuffer
if(file.size < 64 * 1024 * 1024)
{
var loader = new v86util.SyncFileBuffer(file);
loader.onprogress = show_progress.bind(this, { msg: "Loading disk image into memory" });
}
else
{
var loader = new v86util.AsyncFileBuffer(file);
}
loader.onload = function()
{
switch(type)
{
case "floppy":
settings.fda = loader;
break;
case "hd":
settings.hda = loader;
break;
case "cdrom":
settings.cdrom = loader;
break;
}
cb();
}
loader.load();
}
$("toggle_mouse").onclick = function()
{
var mouse_adapter = settings.mouse_adapter;
if(mouse_adapter)
{
var state = mouse_adapter.emu_enabled = !mouse_adapter.emu_enabled;
$("toggle_mouse").value = (state ? "Dis" : "En") + "able mouse";
}
};
$("lock_mouse").onclick = function()
{
var mouse_adapter = settings.mouse_adapter;
if(mouse_adapter && !mouse_adapter.emu_enabled)
{
$("toggle_mouse").onclick();
}
lock_mouse(document.body);
$("lock_mouse").blur();
};
var biosfile = DEBUG ? "seabios-debug.bin" : "seabios.bin";
var vgabiosfile = DEBUG ? "vgabios-0.7a.debug.bin" : "bochs-vgabios-0.7a.bin";
v86util.load_file("bios/" + biosfile, function(img)
{
settings.bios = img;
start_emulation();
});
v86util.load_file("bios/" + vgabiosfile, function(img)
{
settings.vga_bios = img;
start_emulation();
});
var settings = {};
$("start_emulation").onclick = function()
{
$("boot_options").style.display = "none";
set_profile("custom");
var images = [];
var last_file;
if($("floppy_image").files.length)
var floppy_file = $("floppy_image").files[0];
if(floppy_file)
{
images.push({
file: $("floppy_image").files[0],
type: "floppy",
});
last_file = floppy_file;
settings.fda = { buffer: floppy_file };
}
if($("cd_image").files.length)
var cd_file = $("cd_image").files[0];
if(cd_file)
{
images.push({
file: $("cd_image").files[0],
type: "cdrom",
});
last_file = cd_file;
settings.cdrom = { buffer: cd_file };
}
if($("hd_image").files.length)
var hd_file = $("hd_image").files[0];
if(hd_file)
{
images.push({
file: $("hd_image").files[0],
type: "hd",
});
last_file = hd_file;
settings.hda = { buffer: hd_file };
}
var cont = after(images.length, function(result)
if(last_file)
{
set_profile("custom");
set_title(last_file.name);
}
start_emulation({
settings: settings,
done: function(e) { e.run(); },
});
});
images.forEach(function(image)
{
load_local(image.file, image.type, cont);
start_emulation({
settings: settings,
done: function(emulator) {
emulator.run();
},
});
};
@ -303,21 +178,27 @@
}
var oses = [
//{
// id: "archlinux",
// state: "http://localhost/v86-images/v86state.bin",
// //size: 137 * 1024 * 1024,
// size: 75550474,
// name: "Arch Linux",
// memory_size: 64 * 1024 * 1024,
// vga_memory_size: 8 * 1024 * 1024,
// async_hda: "http://localhost/v86-images/arch3.img",
// async_hda_size: 8 * 1024 * 1024 * 1024,
// filesystem: {
// basefs: "http://localhost/v86-images/fs.json",
// baseurl: "http://localhost/v86-images/arch/",
// },
//},
{
id: "archlinux",
state: "http://localhost/v86-images/v86state.bin",
//state: "http://104.131.53.7:8086/v86state.bin",
//size: 137 * 1024 * 1024,
size: 75550474,
name: "Arch Linux",
memory_size: 64 * 1024 * 1024,
vga_memory_size: 8 * 1024 * 1024,
async_hda: "http://localhost/v86-images/arch3.img",
//async_hda: "https://dl.dropboxusercontent.com/u/61029208/arch3.img",
//async_hda: "http://104.131.53.7:8086/arch3.img",
async_hda_size: 8 * 1024 * 1024 * 1024,
filesystem: {
basefs: "http://localhost/v86-images/fs.json",
baseurl: "http://localhost/v86-images/arch/",
//basefs: "http://104.131.53.7:8086/fs.json",
//baseurl: "http://104.131.53.7:8086/arch/",
},
},
{
id: "freedos",
fda: "images/freedos722.img",
@ -387,88 +268,62 @@
function start_profile(infos)
{
var message = { msg: "Downloading image", total: infos.size };
var image = infos.state || infos.fda || infos.cdrom;
$("boot_options").style.display = "none";
set_title(infos.name);
var start = after(1, function(result)
settings.filesystem = infos.filesystem;
if(infos.state)
{
loaded(infos, settings, result.buffer);
});
v86util.load_file(
image,
function(buffer)
{
start({ buffer: buffer });
},
show_progress.bind(this, message)
);
if(infos.filesystem)
{
settings.fs9p = new FS(infos.filesystem.baseurl);
if(infos.filesystem.basefs)
{
start.count++;
settings.fs9p.OnLoaded = start;
settings.fs9p.LoadFilesystem({
lazyloadimages: [],
earlyload: [],
basefsURL: infos.filesystem.basefs,
});
}
$("reset").style.display = "none";
settings.initial_state = {
url: infos.state,
};
}
set_title(infos.name);
$("boot_options").style.display = "none";
}
settings.fda = {
url: infos.fda,
};
settings.cdrom = {
url: infos.cdrom,
};
if(infos.hda)
{
settings.hda = {
url: infos.hda,
};
}
else if(infos.async_hda)
{
settings.hda = {
url: infos.async_hda,
async: true,
size: infos.async_hda_size,
};
}
function loaded(infos, settings, buffer)
{
settings.memory_size = infos.memory_size;
settings.vga_memory_size = infos.vga_memory_size;
if(infos.async_hda)
{
settings.hda = new v86util.AsyncXHRBuffer(
infos.async_hda,
512,
infos.async_hda_size
);
}
if(infos.fda)
{
settings.fda = new SyncBuffer(buffer);
}
else if(infos.cdrom)
{
settings.cdrom = new SyncBuffer(buffer);
}
//if(infos.async_hda)
//{
// settings.hda = new v86util.AsyncXHRBuffer(
// infos.async_hda,
// 512,
// infos.async_hda_size
// );
//}
start_emulation({
settings: settings,
done: function(emulator)
{
if(infos.state)
{
$("reset").style.display = "none";
emulator.restore_state(buffer);
}
//emulator.send("cpu-run");
emulator.run();
if(query_args["c"])
{
var cmd = query_args["c"] + "\n";
for(var i = 0; i < cmd.length; i++)
{
settings.serial_adapter.send_char(cmd.charCodeAt(i));
}
emulator.serial0_send(query_args["c"] + "\n");
}
}
});
@ -479,12 +334,10 @@
{
// called on window.onload, in debug mode
//settings.fs9p = new FS("http://localhost/v86-images/arch/");
//settings.fs9p.LoadFilesystem({
// lazyloadimages: [],
// earlyload: [],
// basefsURL: "http://localhost/v86-images/fs.json",
//});
settings.filesystem = {
baseurl: "http://localhost/v86-images/arch/",
basefs: "http://localhost/v86-images/fs.json",
};
$("restore_state").onchange = function()
{
@ -556,78 +409,87 @@
onload();
}
var start_emulation = after(3, function(result)
function start_emulation(result)
{
/** @const */
var MB = 1024 * 1024;
var settings = result.settings;
dbg_assert(settings.bios && settings.vga_bios);
var memory_size = settings.memory_size;
//var worker = new Worker("src/browser/worker.js");
//var adapter_bus = WorkerBus.init(worker);
var bus = Bus.create();
var adapter_bus = bus[0];
var device_bus = bus[1];
var emulator = new v86(device_bus);
if(DEBUG)
if(!memory_size)
{
debug_start(emulator);
}
memory_size = parseInt($("memory_size").value, 10) * MB;
// avoid warnings
settings.fdb = undefined;
settings.screen_adapter = new ScreenAdapter($("screen_container"), adapter_bus);;
settings.keyboard_adapter = new KeyboardAdapter(adapter_bus);
settings.mouse_adapter = new MouseAdapter(adapter_bus);
settings.boot_order = parseInt($("boot_order").value, 16);
settings.serial_adapter = new SerialAdapter($("serial"), adapter_bus);
//settings.serial_adapter = new ModemAdapter();
//settings.network_adapter = new NetworkAdapter("ws://localhost:8001/", adapter_bus);
//settings.network_adapter = new NetworkAdapter("ws://relay.widgetry.org/", adapter_bus);
if(!settings.memory_size)
{
var memory_size = parseInt($("memory_size").value, 10) * 1024 * 1024;
if(memory_size >= 16 * 1024 * 1024 && memory_size < 2048 * 1024 * 1024)
{
settings.memory_size = memory_size;
}
else
if(memory_size < 16 * MB || memory_size >= 2048 * MB)
{
alert("Invalid memory size - ignored.");
settings.memory_size = 32 * 1024 * 1024;
memory_size = 32 * MB;
}
}
var vga_memory_size = settings.vga_memory_size;
if(!settings.vga_memory_size)
if(!vga_memory_size)
{
var video_memory_size = parseInt($("video_memory_size").value, 10) * 1024 * 1024;
if(video_memory_size > 64 * 1024 && video_memory_size < 2048 * 1024 * 1024)
{
settings.vga_memory_size = video_memory_size;
}
else
vga_memory_size = parseInt($("video_memory_size").value, 10) * MB;
if(vga_memory_size <= 64 * 1024 || vga_memory_size >= 2048 * MB)
{
alert("Invalid video memory size - ignored.");
settings.vga_memory_size = 8 * 1024 * 1024;
vga_memory_size = 8 * MB;
}
}
init_ui(settings, emulator);
var BIOSPATH = "bios/";
var biosfile = DEBUG ? "seabios-debug.bin" : "seabios.bin";
var vgabiosfile = DEBUG ? "vgabios-0.7a.debug.bin" : "bochs-vgabios-0.7a.bin";
emulator.init(settings);
//settings.fs9p = undefined;
//settings.fda = undefined;
//adapter_bus.send("cpu-init", settings);
var emulator = new V86Starter({
memory_size: memory_size,
vga_memory_size: vga_memory_size,
screen_container: $("screen_container"),
serial_container: $("serial"),
boot_order: parseInt($("boot_order").value, 16) || 0,
network_relay_url: "ws://relay.widgetry.org/",
//network_relay_url: "ws://localhost:8001/",
bios: {
url: BIOSPATH + biosfile,
},
vga_bios: {
url: BIOSPATH + vgabiosfile,
},
fda: settings.fda,
hda: settings.hda,
cdrom: settings.cdrom,
initial_state: settings.initial_state,
filesystem: settings.filesystem,
});
emulator.add_listener("emulator-ready", function()
{
if(DEBUG)
{
debug_start(emulator);
}
init_ui({}, emulator);
//setTimeout(function()
//{
result.done(emulator);
//}, 100);
});
});
emulator.add_listener("download-progress", function(e)
{
show_progress("Downloading images", e);
});
};
function init_ui(settings, emulator)
{
@ -637,16 +499,15 @@
$("runtime_infos").style.display = "block";
document.getElementsByClassName("phone_keyboard")[0].style.display = "block";
if($("news"))
var news_element = $("news");
if(news_element)
{
$("news").style.display = "none";
news_element.style.display = "none";
}
var running = true;
$("run").onclick = function()
{
if(running)
if(emulator.is_running())
{
running_time += Date.now() - last_tick;
$("run").value = "Run";
@ -659,7 +520,6 @@
last_tick = Date.now();
}
running = !running;
$("run").blur();
};
@ -668,6 +528,29 @@
location.href = location.pathname;
};
$("lock_mouse").onclick = function()
{
if(!mouse_is_enabled)
{
$("toggle_mouse").onclick();
}
emulator.lock_mouse();
$("lock_mouse").blur();
};
var mouse_is_enabled = true;
$("toggle_mouse").onclick = function()
{
mouse_is_enabled = !mouse_is_enabled;
emulator.mouse_set_status(mouse_is_enabled);
$("toggle_mouse").value = (mouse_is_enabled ? "Dis" : "En") + "able mouse";
$("toggle_mouse").blur();
};
var time = $("running_time"),
ips = $("speed"),
avg_ips = $("avg_speed"),
@ -678,91 +561,83 @@
function update_info()
{
if(!running)
if(!emulator.is_running())
{
setTimeout(update_info, 1000);
return;
}
var now = Date.now(),
last_ips = (emulator.cpu.timestamp_counter - last_instr_counter) / 1000 | 0;
var stats = emulator.get_statistics();
summed_ips += last_ips
var now = Date.now();
var last_ips = stats.cpu.instruction_counter - last_instr_counter;
summed_ips += last_ips;
running_time += now - last_tick;
last_tick = now;
ips.textContent = last_ips;
avg_ips.textContent = summed_ips / running_time * 1000 | 0;
ips.textContent = last_ips / 1000 | 0;
avg_ips.textContent = summed_ips / running_time | 0;
time.textContent = time2str(running_time / 1000 | 0);
last_instr_counter = emulator.cpu.timestamp_counter;
last_instr_counter = stats.cpu.instruction_counter;
setTimeout(update_info, 1000);
}
$("info_mouse_enabled").textContent = stats.mouse.enabled ? "Yes" : "No";
function update_other_info()
{
if(!running)
if(stats.hda)
{
setTimeout(update_other_info, 1000);
return;
}
$("info_hda_sectors_read").textContent = stats.hda.sectors_read;
$("info_hda_bytes_read").textContent = stats.hda.bytes_read;
var devices = emulator.cpu.devices;
var vga_stats = devices.vga.stats;
if(vga_stats.is_graphical)
{
$("info_vga_mode").textContent = "graphical";
$("info_res").textContent = vga_stats.res_x + "x" + vga_stats.res_y;
$("info_bpp").textContent = vga_stats.bpp;
}
else
{
$("info_vga_mode").textContent = "text";
$("info_res").textContent = "-";
$("info_bpp").textContent = "-";
}
if(settings.mouse_adapter)
{
$("info_mouse_enabled").textContent = settings.mouse_adapter.enabled ? "Yes" : "No";
}
if(devices.hda)
{
var hda_stats = devices.hda.stats;
$("info_hda_sectors_read").textContent = hda_stats.sectors_read;
$("info_hda_bytes_read").textContent = hda_stats.bytes_read;
$("info_hda_sectors_written").textContent = hda_stats.sectors_written;
$("info_hda_bytes_written").textContent = hda_stats.bytes_written;
$("info_hda_status").textContent = hda_stats.loading ? "Loading ..." : "Idle";
$("info_hda_sectors_written").textContent = stats.hda.sectors_written;
$("info_hda_bytes_written").textContent = stats.hda.bytes_written;
$("info_hda_status").textContent = stats.hda.loading ? "Loading ..." : "Idle";
}
else
{
$("info_hda").style.display = "none";
}
if(devices.cdrom)
if(stats.cdrom)
{
var cdrom_stats = devices.cdrom.stats;
$("info_cdrom_sectors_read").textContent = cdrom_stats.sectors_read;
$("info_cdrom_bytes_read").textContent = cdrom_stats.bytes_read;
$("info_cdrom_status").textContent = cdrom_stats.loading ? "Loading ..." : "Idle";
$("info_cdrom_sectors_read").textContent = stats.cdrom.sectors_read;
$("info_cdrom_bytes_read").textContent = stats.cdrom.bytes_read;
$("info_cdrom_status").textContent = stats.cdrom.loading ? "Loading ..." : "Idle";
}
else
{
$("info_cdrom").style.display = "none";
}
setTimeout(update_other_info, 1000);
if(stats.vga.is_graphical)
{
$("info_vga_mode").textContent = "Graphical";
$("info_res").textContent = stats.vga.res_x + "x" + stats.vga.res_y;
$("info_bpp").textContent = stats.vga.bpp;
}
else
{
$("info_vga_mode").textContent = "Text";
$("info_res").textContent = "-";
$("info_bpp").textContent = "-";
}
setTimeout(update_info, 1000);
}
setTimeout(update_info, 1000);
setTimeout(update_other_info, 0);
var stats = emulator.get_statistics();
if(!stats.cdrom)
{
$("info_cdrom").style.display = "none";
}
if(!stats.hda)
{
$("info_hda").style.display = "none";
}
$("reset").onclick = function()
{
@ -799,28 +674,28 @@
$("ctrlaltdel").onclick = function()
{
var ps2 = emulator.cpu.devices.ps2;
emulator.keyboard_send_scancodes([
0x1D, // ctrl
0x38, // alt
0x53, // delete
ps2.kbd_send_code(0x1D); // ctrl
ps2.kbd_send_code(0x38); // alt
ps2.kbd_send_code(0x53); // delete
// break codes
ps2.kbd_send_code(0x1D | 0x80);
ps2.kbd_send_code(0x38 | 0x80);
ps2.kbd_send_code(0x53 | 0x80);
// break codes
0x1D | 0x80,
0x38 | 0x80,
0x53 | 0x80,
]);
$("ctrlaltdel").blur();
};
$("alttab").onclick = function()
{
var ps2 = emulator.cpu.devices.ps2;
ps2.kbd_send_code(0x38); // alt
ps2.kbd_send_code(0x0F); // tab
ps2.kbd_send_code(0x38 | 0x80);
ps2.kbd_send_code(0x0F | 0x80);
emulator.keyboard_send_scancodes([
0x38, // alt
0x0F, // tab
0x38 | 0x80,
0x0F | 0x80,
]);
$("alttab").blur();
};
@ -831,30 +706,13 @@
if(n || n > 0)
{
settings.screen_adapter.set_scale(n, n);
emulator.screen_set_scale(n, n);
}
};
$("fullscreen").onclick = function()
{
var elem = document.getElementById("screen_container"),
// bracket notation because otherwise they get renamed by closure compiler
fn = elem["requestFullScreen"] ||
elem["webkitRequestFullscreen"] ||
elem["mozRequestFullScreen"] ||
elem["msRequestFullScreen"];
if(fn)
{
fn.call(elem);
// This is necessary, because otherwise chromium keyboard doesn't work anymore.
// Might (but doesn't seem to) break something else
document.getElementsByClassName("phone_keyboard")[0].focus();
}
lock_mouse(elem);
emulator.screen_go_fullscreen();
};
$("screen_container").onclick = function()
@ -868,15 +726,12 @@
$("take_screenshot").onclick = function()
{
settings.screen_adapter.make_screenshot();
emulator.screen_make_screenshot();
$("take_screenshot").blur();
};
if(settings.serial_adapter)
{
$("serial").style.display = "block";
}
$("serial").style.display = "block";
window.addEventListener("keydown", ctrl_w_rescue, false);
window.addEventListener("keyup", ctrl_w_rescue, false);
@ -902,7 +757,7 @@
function debug_start(emulator)
{
// called as soon as soon as emulation is started, in debug mode
var debug = emulator.cpu.debug;
var debug = emulator.v86.cpu.debug;
$("step").onclick = debug.step.bind(debug);
$("run_until").onclick = debug.run_until.bind(debug);
@ -912,6 +767,7 @@
$("dump_regs").onclick = debug.dump_regs.bind(debug);
$("dump_pt").onclick = debug.dump_page_directory.bind(debug);
$("dump_instructions").onclick = debug.dump_instructions.bind(debug);
$("dump_instructions_file").onclick = function()
{
var ins = debug.get_instructions();
@ -930,11 +786,24 @@
$("save_state").onclick = function()
{
dump_file(emulator.save_state(), "v86-state.bin");
emulator.save_state(function(error, result)
{
if(error)
{
console.log("Couldn't save state: ", error);
}
else
{
dump_file(result, "v86state.bin");
}
});
$("save_state").blur();
};
// helps debugging
window.emulator = emulator;
window.cpu = emulator.v86.cpu;
}
function onpopstate(e)
@ -950,4 +819,5 @@
}
}
})();

474
src/browser/starter.js Normal file
View file

@ -0,0 +1,474 @@
"use strict";
/** @constructor */
function V86Starter(options)
{
this.screen_adapter = undefined;
var bus = Bus.create();
var adapter_bus = this.bus = bus[0];
this.emulator_bus = bus[1];
var emulator = this.v86 = new v86(bus[1]);
var settings = {};
settings.load_devices = true;
settings.memory_size = options["memory_size"];
settings.vga_memory_size = options["vga_memory_size"];
settings.boot_order = options["boot_order"] || 0x213;
settings.fda = undefined;
settings.fdb = undefined;
if(options["network_relay_url"])
{
settings.network_adapter = new NetworkAdapter(options["network_relay_url"], adapter_bus);
}
if(!options["disable_keyboard"])
{
this.keyboard_adapter = new KeyboardAdapter(adapter_bus);
}
if(!options["disable_mouse"])
{
this.mouse_adapter = new MouseAdapter(adapter_bus);
}
if(options["screen_container"])
{
this.screen_adapter = new ScreenAdapter(options["screen_container"], adapter_bus);
}
if(options["serial_container"])
{
this.serial_adapter = new SerialAdapter(options["serial_container"], adapter_bus);
}
//settings.serial_adapter = new ModemAdapter();
var files_to_load = [];
function add_file(file, handler)
{
if(!file)
{
return;
}
if(file.buffer)
{
console.assert(file.buffer instanceof ArrayBuffer || file.buffer instanceof File);
handler(file.buffer);
}
else if(file.url)
{
if(file.async)
{
handler(file);
}
else
{
files_to_load.push({
url: file.url,
handler: handler,
size: file.size,
});
}
}
}
function put_on_settings(name, buffer)
{
switch(name)
{
case "hda":
settings.hda = buffer;
break;
case "hdb":
settings.hdb = buffer;
break;
case "cdrom":
settings.cdrom = buffer;
break;
case "fda":
settings.fda = buffer;
break;
case "fdb":
settings.fdb = buffer;
break;
case "bios":
settings.bios = buffer;
break;
case "vga_bios":
settings.vga_bios = buffer;
break;
default:
dbg_assert(false, name);
}
}
function make_sync_buffer(name, buffer)
{
if(buffer instanceof ArrayBuffer)
{
var result = new SyncBuffer(buffer);
}
else if(buffer instanceof File)
{
// SyncFileBuffer:
// - loads the whole disk image into memory, impossible for large files (more than 1GB)
// - can later serve get/set operations fast and synchronously
// - takes some time for first load, neglectable for small files (up to 100Mb)
//
// AsyncFileBuffer:
// - loads slices of the file asynchronously as requested
// - slower get/set
// Heuristics: If file is smaller than 64M, use SyncFileBuffer
//if(file.size < 64 * 1024 * 1024)
var result = new v86util.AsyncFileBuffer(buffer);
//settings[name] = new SyncFileBuffer(buffer);
}
else if(buffer.async)
{
var result = new v86util.AsyncXHRBuffer(buffer.url, 512, buffer.size);
}
else
{
console.assert(false);
}
put_on_settings(name, result);
}
add_file(options["bios"], put_on_settings.bind(this, "bios"));
add_file(options["vga_bios"], put_on_settings.bind(this, "vga_bios"));
add_file(options["cdrom"], make_sync_buffer.bind(this, "cdrom"));
add_file(options["hda"], make_sync_buffer.bind(this, "hda"));
add_file(options["hdb"], make_sync_buffer.bind(this, "hdb"));
add_file(options["fda"], make_sync_buffer.bind(this, "fda"));
add_file(options["fdb"], make_sync_buffer.bind(this, "fdb"));
if(options.filesystem)
{
var fs9p = new FS(options.filesystem.baseurl);
settings.fs9p = fs9p;
//add_file(infos.filesystem.basefs, function()
//{
fs9p.LoadFilesystem({
basefsURL: options.filesystem.basefs,
});
//});
}
var initial_state_buffer;
if(options["initial_state"])
{
add_file(options["initial_state"], function(buffer)
{
console.log(options["initial_state"], buffer);
initial_state_buffer = buffer;
});
}
var starter = this;
cont(0);
function cont(index)
{
var total = files_to_load.length;
if(index < total)
{
var f = files_to_load[index];
v86util.load_file(f.url, function done(result)
{
f.handler(result);
cont(index + 1);
}, function progress(e)
{
starter.emulator_bus.send("download-progress", {
file_index: index,
file_count: total,
lengthComputable: e.lengthComputable,
total: f.size || e.total,
loaded: e.loaded,
});
});
}
else
{
emulator.init(settings);
if(initial_state_buffer)
{
emulator.restore_state(initial_state_buffer);
}
if(options["autostart"])
{
emulator.run();
}
}
}
}
/**
* Start emulation. Do nothing if emulator is running already.
*/
V86Starter.prototype.run = function()
{
this.v86.run();
};
/**
* Stop emulation. Do nothing if emulator is not running.
*/
V86Starter.prototype.stop = function()
{
this.v86.stop();
};
/**
* Restart (force a reboot).
*/
V86Starter.prototype.restart = function()
{
this.v86.restart();
};
/**
* @param {string} event
* @param {function(*)} listener
*/
V86Starter.prototype.add_listener = function(event, listener)
{
this.bus.register(event, listener, this);
};
/**
* @param {string} event
* @param {function(*)} listener
*/
V86Starter.prototype.remove_listener = function(event, listener)
{
this.bus.unregister(event, listener);
};
/**
* @param {ArrayBuffer} state
*/
V86Starter.prototype.restore_state = function(state)
{
this.v86.restore_state(state);
};
/**
* @param {function(Object, ArrayBuffer)} callback
*/
V86Starter.prototype.save_state = function(callback)
{
// Might become asynchronous at some point
var emulator = this;
setTimeout(function()
{
callback(null, emulator.v86.save_state());
}, 0);
};
/**
* @return {Object}
*/
V86Starter.prototype.get_statistics = function()
{
var stats = {
cpu: {
instruction_counter: this.v86.cpu.timestamp_counter,
},
};
var devices = this.v86.cpu.devices;
if(devices.hda)
{
stats.hda = devices.hda.stats;
}
if(devices.cdrom)
{
stats.cdrom = devices.cdrom.stats;
}
if(devices.ps2)
{
stats.mouse = {
enabled: devices.ps2.enable_mouse,
};
}
if(devices.vga)
{
stats.vga = devices.vga.stats;
}
return stats;
};
/**
* @return {boolean}
*/
V86Starter.prototype.is_running = function()
{
return this.v86.running;
};
/**
* @param {Array.<number>} codes
*/
V86Starter.prototype.keyboard_send_scancodes = function(codes)
{
var ps2 = this.v86.cpu.devices.ps2;
for(var i = 0; i < codes.length; i++)
{
ps2.kbd_send_code(codes[i]);
}
};
/**
* Download a screenshot
*/
V86Starter.prototype.screen_make_screenshot = function()
{
if(this.screen_adapter)
{
this.screen_adapter.make_screenshot();
}
};
/**
* Set the scaling level of the emulated screen
*
* @param {number} sx
* @param {number} sy
*/
V86Starter.prototype.screen_set_scale = function(sx, sy)
{
if(this.screen_adapter)
{
this.screen_adapter.set_scale(sx, sy);
}
};
/**
* Make the browser go fullscreen
*/
V86Starter.prototype.screen_go_fullscreen = function()
{
if(!this.screen_adapter)
{
return;
}
var elem = document.getElementById("screen_container");
if(!elem)
{
return;
}
// bracket notation because otherwise they get renamed by closure compiler
var fn = elem["requestFullScreen"] ||
elem["webkitRequestFullscreen"] ||
elem["mozRequestFullScreen"] ||
elem["msRequestFullScreen"];
if(fn)
{
fn.call(elem);
// This is necessary, because otherwise chromium keyboard doesn't work anymore.
// Might (but doesn't seem to) break something else
var focus_element = document.getElementsByClassName("phone_keyboard")[0];
focus_element && focus_element.focus();
}
//this.lock_mouse(elem);
this.lock_mouse();
};
/**
* Lock the mouse button (it is inivisble and movements are only registered in
* the emulator
*/
V86Starter.prototype.lock_mouse = function()
{
var elem = document.body;
var fn = elem["requestPointerLock"] ||
elem["mozRequestPointerLock"] ||
elem["webkitRequestPointerLock"];
if(fn)
{
fn.call(elem);
}
};
/**
* Enable or disable sending mouse events to the emulated PS2 controller
*
* @param {boolean} enabled
*/
V86Starter.prototype.mouse_set_status = function(enabled)
{
if(this.mouse_adapter)
{
this.mouse_adapter.emu_enabled = enabled;
}
};
/**
* Send a string to the first emulated serial terminal
*
* @param {string} data
*/
V86Starter.prototype.serial0_send = function(data)
{
for(var i = 0; i < data.length; i++)
{
this.serial_adapter.send_char(data.charCodeAt(i));
}
};
// Closure Compiler's way of exporting
if(typeof window !== "undefined")
{
window["V86Starter"] = V86Starter;
V86Starter.prototype["run"] = V86Starter.prototype.run;
V86Starter.prototype["stop"] = V86Starter.prototype.stop;
V86Starter.prototype["restart"] = V86Starter.prototype.restart;
V86Starter.prototype["add_listener"] = V86Starter.prototype.add_listener;
V86Starter.prototype["remove_listener"] = V86Starter.prototype.remove_listener;
V86Starter.prototype["restore_state"] = V86Starter.prototype.restore_state;
V86Starter.prototype["save_state"] = V86Starter.prototype.save_state;
V86Starter.prototype["get_statistics"] = V86Starter.prototype.get_statistics;
V86Starter.prototype["is_running"] = V86Starter.prototype.is_running;
V86Starter.prototype["keyboard_send_scancodes"] = V86Starter.prototype.keyboard_send_scancodes;
V86Starter.prototype["screen_make_screenshot"] = V86Starter.prototype.screen_make_screenshot;
V86Starter.prototype["screen_set_scale"] = V86Starter.prototype.screen_set_scale;
V86Starter.prototype["screen_go_fullscreen"] = V86Starter.prototype.screen_go_fullscreen;
V86Starter.prototype["lock_mouse"] = V86Starter.prototype.lock_mouse;
V86Starter.prototype["mouse_set_status"] = V86Starter.prototype.mouse_set_status;
V86Starter.prototype["serial0_send"] = V86Starter.prototype.serial0_send;
}

View file

@ -48,11 +48,6 @@ function h(n, len)
}
}
if(typeof window === "object")
{
window["SyncBuffer"] = SyncBuffer;
}
/**
* Synchronous access to ArrayBuffer
* @constructor

View file

@ -84,6 +84,7 @@ v86.prototype.init = function(settings)
}
this.cpu.init(settings, this.bus);
this.bus.send_async("emulator-ready");
};
// initialization that only needs to be once
@ -157,11 +158,13 @@ v86.prototype.lazy_init = function()
v86.prototype.save_state = function()
{
// TODO: Should be implemented here, not on cpu
return this.cpu.save_state();
};
v86.prototype.restore_state = function(state)
{
// TODO: Should be implemented here, not on cpu
return this.cpu.restore_state(state);
};