123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353 |
- "use strict";
- /**
- * Constructor for emulator instances.
- *
- * Usage: `var emulator = new V86Starter(options);`
- *
- * Options can have the following properties (all optional, default in parenthesis):
- *
- * - `memory_size number` (16 * 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.
- *
- * - `autostart boolean` (false) - If emulation should be started when emulator
- * is ready.
- *
- * - `disable_keyboard boolean` (false) - If the keyboard should be disabled.
- * - `disable_mouse boolean` (false) - If the mouse should be disabled.
- *
- * - `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.
- *
- * - `bios Object` (No bios) - Either a url pointing to a bios or an
- * ArrayBuffer, see below.
- * - `vga_bios Object` (No VGA bios) - VGA bios, see below.
- * - `hda Object` (No hard drive) - First hard disk, see below.
- * - `fda Object` (No floppy disk) - First floppy disk, see below.
- * - `cdrom Object` (No CD) - See below.
- *
- * - `bzimage Object` - A Linux kernel image to boot (only bzimage format), see below.
- * - `initrd Object` - A Linux ramdisk image, see below.
- * - `bzimage_initrd_from_filesystem boolean` - Automatically fetch bzimage and
- * initrd from the specified `filesystem`.
- *
- * - `initial_state Object` (Normal boot) - An initial state to load, see
- * [`restore_state`](#restore_statearraybuffer-state) and below.
- *
- * - `filesystem Object` (No 9p filesystem) - A 9p filesystem, see
- * [filesystem.md](filesystem.md).
- *
- * - `serial_container HTMLTextAreaElement` (No serial terminal) - A textarea
- * that will receive and send data to the emulated serial terminal.
- * Alternatively the serial terminal can also be accessed programatically,
- * 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).
- *
- * ***
- *
- * There are two ways to load images (`bios`, `vga_bios`, `cdrom`, `hda`, ...):
- *
- * - Pass an object that has a url. Optionally, `async: true` and `size:
- * size_in_bytes` can be added to the object, so that sectors of the image
- * are loaded on demand instead of being loaded before boot (slower, but
- * strongly recommended for big files). In that case, the `Range: bytes=...`
- * header must be supported on the server.
- *
- * ```javascript
- * // download file before boot
- * bios: {
- * url: "bios/seabios.bin"
- * }
- * // download file sectors as requested, size is required
- * hda: {
- * url: "disk/linux.iso",
- * async: true,
- * size: 16 * 1024 * 1024
- * }
- * ```
- *
- * - Pass an `ArrayBuffer` or `File` object as `buffer` property.
- *
- * ```javascript
- * // use <input type=file>
- * bios: {
- * buffer: document.all.hd_image.files[0]
- * }
- * // start with empty hard drive
- * hda: {
- * buffer: new ArrayBuffer(16 * 1024 * 1024)
- * }
- * ```
- *
- * ***
- *
- * @param {Object} options Options to initialize the emulator with.
- * @constructor
- */
- function V86Starter(options)
- {
- //var worker = new Worker("src/browser/worker.js");
- //var adapter_bus = this.bus = WorkerBus.init(worker);
- this.cpu_is_running = false;
- const bus = Bus.create();
- const adapter_bus = this.bus = bus[0];
- this.emulator_bus = bus[1];
- var cpu;
- var wasm_memory;
- const wasm_table = new WebAssembly.Table({ element: "anyfunc", "initial": WASM_TABLE_SIZE + WASM_TABLE_OFFSET });
- const wasm_shared_funcs = {
- "cpu_exception_hook": (n) => {
- return this["cpu_exception_hook"] && this["cpu_exception_hook"](n);
- },
- "hlt_op": function() { return cpu.hlt_op(); },
- "abort": function() { dbg_assert(false); },
- "microtick": v86.microtick,
- "get_rand_int": function() { return v86util.get_rand_int(); },
- "pic_acknowledge": function() { cpu.pic_acknowledge(); },
- "io_port_read8": function(addr) { return cpu.io.port_read8(addr); },
- "io_port_read16": function(addr) { return cpu.io.port_read16(addr); },
- "io_port_read32": function(addr) { return cpu.io.port_read32(addr); },
- "io_port_write8": function(addr, value) { cpu.io.port_write8(addr, value); },
- "io_port_write16": function(addr, value) { cpu.io.port_write16(addr, value); },
- "io_port_write32": function(addr, value) { cpu.io.port_write32(addr, value); },
- "mmap_read8": function(addr) { return cpu.mmap_read8(addr); },
- "mmap_read16": function(addr) { return cpu.mmap_read16(addr); },
- "mmap_read32": function(addr) { return cpu.mmap_read32(addr); },
- "mmap_write8": function(addr, value) { cpu.mmap_write8(addr, value); },
- "mmap_write16": function(addr, value) { cpu.mmap_write16(addr, value); },
- "mmap_write32": function(addr, value) { cpu.mmap_write32(addr, value); },
- "mmap_write64": function(addr, value0, value1) { cpu.mmap_write64(addr, value0, value1); },
- "mmap_write128": function(addr, value0, value1, value2, value3) {
- cpu.mmap_write128(addr, value0, value1, value2, value3);
- },
- "log_from_wasm": function(offset, len) {
- const str = v86util.read_sized_string_from_mem(wasm_memory, offset, len);
- dbg_log(str, LOG_CPU);
- },
- "console_log_from_wasm": function(offset, len) {
- const str = v86util.read_sized_string_from_mem(wasm_memory, offset, len);
- console.error(str);
- },
- "dbg_trace_from_wasm": function() {
- dbg_trace(LOG_CPU);
- },
- "codegen_finalize": (wasm_table_index, start, state_flags, ptr, len) => {
- cpu.codegen_finalize(wasm_table_index, start, state_flags, ptr, len);
- },
- "jit_clear_func": (wasm_table_index) => cpu.jit_clear_func(wasm_table_index),
- "jit_clear_all_funcs": () => cpu.jit_clear_all_funcs(),
- "__indirect_function_table": wasm_table,
- };
- let wasm_fn = options["wasm_fn"];
- if(!wasm_fn)
- {
- wasm_fn = env =>
- {
- return new Promise(resolve => {
- let v86_bin = DEBUG ? "v86-debug.wasm" : "v86.wasm";
- let v86_bin_fallback = "v86-fallback.wasm";
- if(options["wasm_path"])
- {
- v86_bin = options["wasm_path"];
- const slash = v86_bin.lastIndexOf("/");
- const dir = slash === -1 ? "" : v86_bin.substr(0, slash);
- v86_bin_fallback = dir + "/" + v86_bin_fallback;
- }
- else if(typeof window === "undefined" && typeof __dirname === "string")
- {
- v86_bin = __dirname + "/" + v86_bin;
- v86_bin_fallback = __dirname + "/" + v86_bin_fallback;
- }
- else
- {
- v86_bin = "build/" + v86_bin;
- v86_bin_fallback = "build/" + v86_bin_fallback;
- }
- v86util.load_file(v86_bin, {
- done: async bytes =>
- {
- try
- {
- const { instance } = await WebAssembly.instantiate(bytes, env);
- resolve(instance.exports);
- }
- catch(err)
- {
- v86util.load_file(v86_bin_fallback, {
- done: async bytes => {
- const { instance } = await WebAssembly.instantiate(bytes, env);
- resolve(instance.exports);
- },
- });
- }
- },
- progress: e =>
- {
- this.emulator_bus.send("download-progress", {
- file_index: 0,
- file_count: 1,
- file_name: v86_bin,
- lengthComputable: e.lengthComputable,
- total: e.total,
- loaded: e.loaded,
- });
- }
- });
- });
- };
- }
- wasm_fn({ "env": wasm_shared_funcs })
- .then((exports) => {
- wasm_memory = exports.memory;
- exports["rust_init"]();
- const emulator = this.v86 = new v86(this.emulator_bus, { exports, wasm_table });
- cpu = emulator.cpu;
- this.continue_init(emulator, options);
- });
- }
- V86Starter.prototype.continue_init = async function(emulator, options)
- {
- this.bus.register("emulator-stopped", function()
- {
- this.cpu_is_running = false;
- }, this);
- this.bus.register("emulator-started", function()
- {
- this.cpu_is_running = true;
- }, this);
- var settings = {};
- this.disk_images = {
- "fda": undefined,
- "fdb": undefined,
- "hda": undefined,
- "hdb": undefined,
- "cdrom": undefined,
- };
- settings.acpi = options["acpi"];
- settings.load_devices = true;
- settings.log_level = options["log_level"];
- settings.memory_size = options["memory_size"] || 64 * 1024 * 1024;
- settings.vga_memory_size = options["vga_memory_size"] || 8 * 1024 * 1024;
- settings.boot_order = options["boot_order"] || 0x213;
- settings.fastboot = options["fastboot"] || false;
- settings.fda = undefined;
- settings.fdb = undefined;
- settings.uart1 = options["uart1"];
- settings.uart2 = options["uart2"];
- settings.uart3 = options["uart3"];
- settings.cmdline = options["cmdline"];
- settings.preserve_mac_from_state_image = options["preserve_mac_from_state_image"];
- settings.mac_address_translation = options["mac_address_translation"];
- settings.cpuid_level = options["cpuid_level"];
- if(options["network_adapter"])
- {
- this.network_adapter = options["network_adapter"](this.bus);
- }
- else if(options["network_relay_url"])
- {
- this.network_adapter = new NetworkAdapter(options["network_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;
- 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"]);
- }
- if(options["screen_container"])
- {
- this.screen_adapter = new ScreenAdapter(options["screen_container"], this.bus);
- }
- else if(options["screen_dummy"])
- {
- this.screen_adapter = new DummyScreenAdapter(this.bus);
- }
- if(options["serial_container"])
- {
- this.serial_adapter = new SerialAdapter(options["serial_container"], this.bus);
- //this.recording_adapter = new SerialRecordingAdapter(this.bus);
- }
- if(options["serial_container_xtermjs"])
- {
- this.serial_adapter = new SerialAdapterXtermJS(options["serial_container_xtermjs"], this.bus);
- }
- if(!options["disable_speaker"])
- {
- this.speaker_adapter = new SpeakerAdapter(this.bus);
- }
- // ugly, but required for closure compiler compilation
- function put_on_settings(name, buffer)
- {
- switch(name)
- {
- case "hda":
- settings.hda = this.disk_images["hda"] = buffer;
- break;
- case "hdb":
- settings.hdb = this.disk_images["hdb"] = buffer;
- break;
- case "cdrom":
- settings.cdrom = this.disk_images["cdrom"] = buffer;
- break;
- case "fda":
- settings.fda = this.disk_images["fda"] = buffer;
- break;
- case "fdb":
- settings.fdb = this.disk_images["fdb"] = buffer;
- break;
- case "multiboot":
- settings.multiboot = this.disk_images["multiboot"] = buffer.buffer;
- break;
- case "bzimage":
- settings.bzimage = this.disk_images["bzimage"] = buffer.buffer;
- break;
- case "initrd":
- settings.initrd = this.disk_images["initrd"] = buffer.buffer;
- break;
- case "bios":
- settings.bios = buffer.buffer;
- break;
- case "vga_bios":
- settings.vga_bios = buffer.buffer;
- break;
- case "initial_state":
- settings.initial_state = buffer.buffer;
- break;
- case "fs9p_json":
- settings.fs9p_json = buffer;
- break;
- default:
- dbg_assert(false, name);
- }
- }
- var files_to_load = [];
- function add_file(name, file)
- {
- if(!file)
- {
- return;
- }
- if(file.get && file.set && file.load)
- {
- files_to_load.push({
- name: name,
- loadable: file,
- });
- return;
- }
- if(name === "bios" || name === "vga_bios" ||
- name === "initial_state" || name === "multiboot" ||
- name === "bzimage" || name === "initrd")
- {
- // Ignore async for these because they must be available before boot.
- // This should make result.buffer available after the object is loaded
- file.async = false;
- }
- if(file.buffer instanceof ArrayBuffer)
- {
- var buffer = new v86util.SyncBuffer(file.buffer);
- files_to_load.push({
- name: name,
- loadable: buffer,
- });
- }
- else if(typeof File !== "undefined" && file.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 larger than or equal to 256M, use AsyncFileBuffer
- if(file.async === undefined)
- {
- file.async = file.buffer.size >= 256 * 1024 * 1024;
- }
- if(file.async)
- {
- var buffer = new v86util.AsyncFileBuffer(file.buffer);
- }
- else
- {
- var buffer = new v86util.SyncFileBuffer(file.buffer);
- }
- files_to_load.push({
- name: name,
- loadable: buffer,
- });
- }
- else if(file.url)
- {
- if(file.async)
- {
- let buffer;
- if(file.use_parts)
- {
- buffer = new v86util.AsyncXHRPartfileBuffer(file.url, file.size, file.fixed_chunk_size);
- }
- else
- {
- buffer = new v86util.AsyncXHRBuffer(file.url, file.size, file.fixed_chunk_size);
- }
- files_to_load.push({
- name: name,
- loadable: buffer,
- });
- }
- else
- {
- files_to_load.push({
- name: name,
- url: file.url,
- size: file.size,
- });
- }
- }
- else
- {
- dbg_log("Ignored file: url=" + file.url + " buffer=" + file.buffer);
- }
- }
- if(options["state"])
- {
- console.warn("Warning: Unknown option 'state'. Did you mean 'initial_state'?");
- }
- var image_names = [
- "bios", "vga_bios",
- "cdrom", "hda", "hdb", "fda", "fdb",
- "initial_state", "multiboot",
- "bzimage", "initrd",
- ];
- for(var i = 0; i < image_names.length; i++)
- {
- add_file(image_names[i], options[image_names[i]]);
- }
- if(options["filesystem"])
- {
- var fs_url = options["filesystem"].basefs;
- var base_url = options["filesystem"].baseurl;
- let file_storage = new MemoryFileStorage();
- if(base_url)
- {
- file_storage = new ServerFileStorageWrapper(file_storage, base_url);
- }
- settings.fs9p = this.fs9p = new FS(file_storage);
- if(fs_url)
- {
- dbg_assert(base_url, "Filesystem: baseurl must be specified");
- var size;
- if(typeof fs_url === "object")
- {
- size = fs_url.size;
- fs_url = fs_url.url;
- }
- dbg_assert(typeof fs_url === "string");
- files_to_load.push({
- name: "fs9p_json",
- url: fs_url,
- size: size,
- as_json: true,
- });
- }
- }
- var starter = this;
- var total = files_to_load.length;
- var cont = function(index)
- {
- if(index === total)
- {
- setTimeout(done.bind(this), 0);
- return;
- }
- var f = files_to_load[index];
- if(f.loadable)
- {
- f.loadable.onload = function(e)
- {
- put_on_settings.call(this, f.name, f.loadable);
- cont(index + 1);
- }.bind(this);
- f.loadable.load();
- }
- else
- {
- v86util.load_file(f.url, {
- done: function(result)
- {
- put_on_settings.call(this, f.name, f.as_json ? result : new v86util.SyncBuffer(result));
- cont(index + 1);
- }.bind(this),
- progress: function progress(e)
- {
- if(e.target.status === 200)
- {
- starter.emulator_bus.send("download-progress", {
- file_index: index,
- file_count: total,
- file_name: f.url,
- lengthComputable: e.lengthComputable,
- total: e.total || f.size,
- loaded: e.loaded,
- });
- }
- else
- {
- starter.emulator_bus.send("download-error", {
- file_index: index,
- file_count: total,
- file_name: f.url,
- request: e.target,
- });
- }
- },
- as_json: f.as_json,
- });
- }
- }.bind(this);
- cont(0);
- async function done()
- {
- //if(settings.initial_state)
- //{
- // // avoid large allocation now, memory will be restored later anyway
- // settings.memory_size = 0;
- //}
- if(settings.fs9p && settings.fs9p_json)
- {
- if(!settings.initial_state)
- {
- settings.fs9p.load_from_json(settings.fs9p_json);
- }
- else
- {
- dbg_log("Filesystem basefs ignored: Overridden by state image");
- }
- if(options["bzimage_initrd_from_filesystem"])
- {
- const { bzimage_path, initrd_path } = this.get_bzimage_initrd_from_filesystem(settings.fs9p);
- dbg_log("Found bzimage: " + bzimage_path + " and initrd: " + initrd_path);
- const [initrd, bzimage] = await Promise.all([
- settings.fs9p.read_file(initrd_path),
- settings.fs9p.read_file(bzimage_path),
- ]);
- put_on_settings.call(this, "initrd", new v86util.SyncBuffer(initrd.buffer));
- put_on_settings.call(this, "bzimage", new v86util.SyncBuffer(bzimage.buffer));
- finish.call(this);
- }
- else
- {
- finish.call(this);
- }
- }
- else
- {
- dbg_assert(
- !options["bzimage_initrd_from_filesystem"],
- "bzimage_initrd_from_filesystem: Requires a filesystem");
- finish.call(this);
- }
- function finish()
- {
- this.serial_adapter && this.serial_adapter.show && this.serial_adapter.show();
- this.bus.send("cpu-init", settings);
- if(settings.initial_state)
- {
- emulator.restore_state(settings.initial_state);
- // The GC can't free settings, since it is referenced from
- // several closures. This isn't needed anymore, so we delete it
- // here
- settings.initial_state = undefined;
- }
- if(options["autostart"])
- {
- this.bus.send("cpu-run");
- }
- this.emulator_bus.send("emulator-loaded");
- }
- }
- };
- V86Starter.prototype.get_bzimage_initrd_from_filesystem = function(filesystem)
- {
- const root = (filesystem.read_dir("/") || []).map(x => "/" + x);
- const boot = (filesystem.read_dir("/boot/") || []).map(x => "/boot/" + x);
- let initrd_path;
- let bzimage_path;
- for(let f of [].concat(root, boot))
- {
- const old = /old/i.test(f) || /fallback/i.test(f);
- const is_bzimage = /vmlinuz/i.test(f) || /bzimage/i.test(f);
- const is_initrd = /initrd/i.test(f) || /initramfs/i.test(f);
- if(is_bzimage && (!bzimage_path || !old))
- {
- bzimage_path = f;
- }
- if(is_initrd && (!initrd_path || !old))
- {
- initrd_path = f;
- }
- }
- if(!initrd_path || !bzimage_path)
- {
- console.log("Failed to find bzimage or initrd in filesystem. Files:");
- console.log(root.join(" "));
- console.log(boot.join(" "));
- }
- return { initrd_path, bzimage_path };
- };
- /**
- * Start emulation. Do nothing if emulator is running already. Can be
- * asynchronous.
- * @export
- */
- V86Starter.prototype.run = async function()
- {
- this.bus.send("cpu-run");
- };
- /**
- * Stop emulation. Do nothing if emulator is not running. Can be asynchronous.
- * @export
- */
- V86Starter.prototype.stop = async function()
- {
- if(!this.cpu_is_running)
- {
- return;
- }
- await new Promise(resolve => {
- const listener = () => {
- this.remove_listener("emulator-stopped", listener);
- resolve();
- };
- this.add_listener("emulator-stopped", listener);
- this.bus.send("cpu-stop");
- });
- };
- /**
- * @ignore
- * @export
- */
- V86Starter.prototype.destroy = async function()
- {
- await this.stop();
- this.v86.destroy();
- this.keyboard_adapter && this.keyboard_adapter.destroy();
- this.network_adapter && this.network_adapter.destroy();
- this.mouse_adapter && this.mouse_adapter.destroy();
- this.screen_adapter && this.screen_adapter.destroy();
- this.serial_adapter && this.serial_adapter.destroy();
- this.speaker_adapter && this.speaker_adapter.destroy();
- };
- /**
- * Restart (force a reboot).
- * @export
- */
- V86Starter.prototype.restart = function()
- {
- this.bus.send("cpu-restart");
- };
- /**
- * Add an event listener (the emulator is an event emitter). A list of events
- * can be found at [events.md](events.md).
- *
- * 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.
- * @export
- */
- V86Starter.prototype.add_listener = function(event, listener)
- {
- this.bus.register(event, listener, this);
- };
- /**
- * Remove an event listener.
- *
- * @param {string} event
- * @param {function(*)} listener
- * @export
- */
- V86Starter.prototype.remove_listener = function(event, listener)
- {
- this.bus.unregister(event, listener);
- };
- /**
- * Restore the emulator state from the given state, which must be an
- * ArrayBuffer returned by
- * [`save_state`](#save_statefunctionobject-arraybuffer-callback).
- *
- * Note that the state can only be restored correctly if this constructor has
- * been created with the same options as the original instance (e.g., same disk
- * images, memory size, etc.).
- *
- * Different versions of the emulator might use a different format for the
- * state buffer.
- *
- * @param {ArrayBuffer} state
- * @export
- */
- V86Starter.prototype.restore_state = async function(state)
- {
- dbg_assert(arguments.length === 1);
- this.v86.restore_state(state);
- };
- /**
- * Asynchronously save the current state of the emulator.
- *
- * @return {Promise<ArrayBuffer>}
- * @export
- */
- V86Starter.prototype.save_state = async function()
- {
- dbg_assert(arguments.length === 0);
- return this.v86.save_state();
- };
- /**
- * Return an object with several statistics. Return value looks similar to
- * (but can be subject to change in future versions or different
- * configurations, so use defensively):
- *
- * ```javascript
- * {
- * "cpu": {
- * "instruction_counter": 2821610069
- * },
- * "hda": {
- * "sectors_read": 95240,
- * "sectors_written": 952,
- * "bytes_read": 48762880,
- * "bytes_written": 487424,
- * "loading": false
- * },
- * "cdrom": {
- * "sectors_read": 0,
- * "sectors_written": 0,
- * "bytes_read": 0,
- * "bytes_written": 0,
- * "loading": false
- * },
- * "mouse": {
- * "enabled": true
- * },
- * "vga": {
- * "is_graphical": true,
- * "res_x": 800,
- * "res_y": 600,
- * "bpp": 32
- * }
- * }
- * ```
- *
- * @deprecated
- * @return {Object}
- * @export
- */
- V86Starter.prototype.get_statistics = function()
- {
- console.warn("V86Starter.prototype.get_statistics is deprecated. Use events instead.");
- var stats = {
- cpu: {
- instruction_counter: this.get_instruction_counter(),
- },
- };
- if(!this.v86)
- {
- return stats;
- }
- 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.use_mouse,
- };
- }
- if(devices.vga)
- {
- stats["vga"] = {
- "is_graphical": devices.vga.stats.is_graphical,
- };
- }
- return stats;
- };
- /**
- * @return {number}
- * @ignore
- * @export
- */
- V86Starter.prototype.get_instruction_counter = function()
- {
- if(this.v86)
- {
- return this.v86.cpu.instruction_counter[0] >>> 0;
- }
- else
- {
- // TODO: Should be handled using events
- return 0;
- }
- };
- /**
- * @return {boolean}
- * @export
- */
- V86Starter.prototype.is_running = function()
- {
- return this.cpu_is_running;
- };
- /**
- * Send a sequence of scan codes to the emulated PS2 controller. A list of
- * codes can be found at http://stanislavs.org/helppc/make_codes.html.
- * Do nothing if there is no keyboard controller.
- *
- * @param {Array.<number>} codes
- * @export
- */
- V86Starter.prototype.keyboard_send_scancodes = function(codes)
- {
- for(var i = 0; i < codes.length; i++)
- {
- this.bus.send("keyboard-code", codes[i]);
- }
- };
- /**
- * Send translated keys
- * @ignore
- * @export
- */
- V86Starter.prototype.keyboard_send_keys = function(codes)
- {
- for(var i = 0; i < codes.length; i++)
- {
- this.keyboard_adapter.simulate_press(codes[i]);
- }
- };
- /**
- * Send text
- * @ignore
- * @export
- */
- V86Starter.prototype.keyboard_send_text = function(string)
- {
- for(var i = 0; i < string.length; i++)
- {
- this.keyboard_adapter.simulate_char(string[i]);
- }
- };
- /**
- * Download a screenshot.
- *
- * @ignore
- * @export
- */
- 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
- *
- * @ignore
- * @export
- */
- V86Starter.prototype.screen_set_scale = function(sx, sy)
- {
- if(this.screen_adapter)
- {
- this.screen_adapter.set_scale(sx, sy);
- }
- };
- /**
- * Go fullscreen.
- *
- * @ignore
- * @export
- */
- 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();
- }
- try {
- navigator.keyboard.lock();
- } catch(e) {}
- this.lock_mouse();
- };
- /**
- * Lock the mouse cursor: It becomes invisble and is not moved out of the
- * browser window.
- *
- * @ignore
- * @export
- */
- 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;
- }
- };
- /**
- * Enable or disable sending keyboard events to the emulated PS2 controller.
- *
- * @param {boolean} enabled
- * @export
- */
- V86Starter.prototype.keyboard_set_status = function(enabled)
- {
- if(this.keyboard_adapter)
- {
- this.keyboard_adapter.emu_enabled = enabled;
- }
- };
- /**
- * Send a string to the first emulated serial terminal.
- *
- * @param {string} data
- * @export
- */
- V86Starter.prototype.serial0_send = function(data)
- {
- for(var i = 0; i < data.length; i++)
- {
- this.bus.send("serial0-input", data.charCodeAt(i));
- }
- };
- /**
- * Send bytes to a serial port (to be received by the emulated PC).
- *
- * @param {Uint8Array} data
- * @export
- */
- V86Starter.prototype.serial_send_bytes = function(serial, data)
- {
- for(var i = 0; i < data.length; i++)
- {
- this.bus.send("serial" + serial + "-input", data[i]);
- }
- };
- /**
- * Mount another filesystem to the current filesystem.
- * @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
- */
- V86Starter.prototype.mount_fs = async function(path, baseurl, basefs, callback)
- {
- let file_storage = new MemoryFileStorage();
- if(baseurl)
- {
- 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());
- }
- else
- {
- mount();
- }
- };
- /**
- * Write to a file in the 9p filesystem. Nothing happens if no filesystem has
- * been initialized.
- *
- * @param {string} file
- * @param {Uint8Array} data
- * @export
- */
- V86Starter.prototype.create_file = async function(file, data)
- {
- dbg_assert(arguments.length === 2);
- var fs = this.fs9p;
- if(!fs)
- {
- return;
- }
- var parts = file.split("/");
- var filename = parts[parts.length - 1];
- var path_infos = fs.SearchPath(file);
- var parent_id = path_infos.parentid;
- var not_found = filename === "" || parent_id === -1;
- if(!not_found)
- {
- await fs.CreateBinaryFile(filename, parent_id, data);
- }
- else
- {
- return Promise.reject(new FileNotFoundError());
- }
- };
- /**
- * Read a file in the 9p filesystem. Nothing happens if no filesystem has been
- * initialized.
- *
- * @param {string} file
- * @export
- */
- V86Starter.prototype.read_file = async function(file)
- {
- dbg_assert(arguments.length === 1);
- var fs = this.fs9p;
- if(!fs)
- {
- return;
- }
- const result = await fs.read_file(file);
- if(result)
- {
- return result;
- }
- else
- {
- return Promise.reject(new FileNotFoundError());
- }
- };
- V86Starter.prototype.automatically = function(steps)
- {
- const run = (steps) =>
- {
- const step = steps[0];
- if(!step)
- {
- return;
- }
- const remaining_steps = steps.slice(1);
- if(step.sleep)
- {
- setTimeout(() => run(remaining_steps), step.sleep * 1000);
- return;
- }
- if(step.vga_text)
- {
- const screen = this.screen_adapter.get_text_screen();
- for(let line of screen)
- {
- if(line.includes(step.vga_text))
- {
- run(remaining_steps);
- return;
- }
- }
- setTimeout(() => run(steps), 1000);
- return;
- }
- if(step.keyboard_send)
- {
- if(step.keyboard_send instanceof Array)
- {
- this.keyboard_send_scancodes(step.keyboard_send);
- }
- else
- {
- dbg_assert(typeof step.keyboard_send === "string");
- this.keyboard_send_text(step.keyboard_send);
- }
- run(remaining_steps);
- return;
- }
- if(step.call)
- {
- step.call();
- run(remaining_steps);
- return;
- }
- dbg_assert(false, step);
- };
- run(steps);
- };
- /**
- * Reads data from memory at specified offset.
- *
- * @param {number} offset
- * @param {number} length
- * @returns
- */
- V86Starter.prototype.read_memory = function(offset, length)
- {
- return this.v86.cpu.read_blob(offset, length);
- };
- /**
- * Writes data to memory at specified offset.
- *
- * @param {Array.<number>|Uint8Array} blob
- * @param {number} offset
- */
- V86Starter.prototype.write_memory = function(blob, offset)
- {
- this.v86.cpu.write_blob(blob, offset);
- };
- /**
- * @ignore
- * @constructor
- *
- * @param {string=} message
- */
- function FileExistsError(message)
- {
- this.message = message || "File already exists";
- }
- FileExistsError.prototype = Error.prototype;
- /**
- * @ignore
- * @constructor
- *
- * @param {string=} message
- */
- function FileNotFoundError(message)
- {
- this.message = message || "File not found";
- }
- FileNotFoundError.prototype = Error.prototype;
- // Closure Compiler's way of exporting
- if(typeof window !== "undefined")
- {
- window["V86Starter"] = V86Starter;
- window["V86"] = V86Starter;
- }
- else if(typeof module !== "undefined" && typeof module.exports !== "undefined")
- {
- module.exports["V86Starter"] = V86Starter;
- module.exports["V86"] = V86Starter;
- }
- else if(typeof importScripts === "function")
- {
- // web worker
- self["V86Starter"] = V86Starter;
- self["V86"] = V86Starter;
- }
|