blob: 47d0e2e23c21817893966bfad9b9256ba91dabfd [file] [log] [blame] [raw]
"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;
}