12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196 |
- "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.
- * - `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
- * options.bios = {
- * url: "bios/seabios.bin"
- * }
- * // download file sectors as requested, size is required
- * options.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>
- * options.bios = {
- * buffer: document.all.hd_image.files[0]
- * }
- * // start with empty hard drive
- * options.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;
- var bus = Bus.create();
- var adapter_bus = this.bus = bus[0];
- this.emulator_bus = bus[1];
- var emulator;
- var cpu;
- var mem;
- var mem8;
- const coverage_logger = new CoverageLogger();
- if(coverage_logger.ENABLED)
- {
- this.bus.register("emulator-stopped", function()
- {
- coverage_logger.dump_to_files();
- }, this);
- }
- var wasm_shared_funcs = {
- "___assert_fail": (condition, file, line, fun) => {
- const memory = mem8;
- function read_string(memory, offset)
- {
- memory = memory.subarray(offset);
- const zero = memory.indexOf(0);
- if(zero !== -1)
- {
- memory = memory.subarray(0, zero);
- }
- return String.fromCharCode.apply(String, memory);
- }
- console.error("Assertion Failed: '%s' at %s:%d in %s",
- read_string(memory, condition),
- read_string(memory, file),
- line,
- read_string(memory, fun));
- dbg_assert(false);
- },
- "_throw_cpu_exception": () => {
- throw MAGIC_CPU_EXCEPTION;
- },
- "_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); },
- "__dbg_trace": function() { return dbg_trace(); },
- "_logop": function(eip, op) { return cpu.debug.logop(eip, op); },
- "_todo": function() { return cpu.todo.apply(cpu, arguments); },
- "_undefined_instruction": function() { return cpu.undefined_instruction.apply(cpu, arguments); },
- "_unimplemented_sse": function() { return cpu.unimplemented_sse(); },
- "_microtick": v86.microtick,
- "_get_rand_int": function() { return v86util.get_rand_int(); },
- "_has_rand_int": function() { return v86util.has_rand_int(); },
- "_printf": function(format_string_offset, stack_top) {
- dbg_assert(arguments.length === 2);
- dbg_log_wasm(mem, format_string_offset, stack_top);
- },
- "_memcpy_large": function(dest, source, length) {
- mem8.set(mem8.subarray(source, source + length), dest);
- return dest;
- },
- "_memcpy": function(dest, source, length) {
- mem8.set(mem8.subarray(source, source + length), dest);
- return dest;
- },
- "_call_interrupt_vector": function(interrupt_nr, is_software_int, has_error_code, error_code) {
- cpu.call_interrupt_vector(interrupt_nr, is_software_int, !!has_error_code, error_code);
- },
- "_far_jump": function(eip, selector, is_call) { return cpu.far_jump(eip, selector, !!is_call); },
- "_far_return": function(eip, selector, stack_adjust) { return cpu.far_return(eip, selector, stack_adjust); },
- "_switch_seg": function(reg, selector) { cpu.switch_seg(reg, selector); },
- "_iret16": function() { return cpu.iret16(); },
- "_iret32": function() { return cpu.iret32(); },
- "_handle_irqs": function() { return cpu.handle_irqs(); },
- "_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) { return cpu.mmap_write8(addr, value); },
- "_mmap_write16": function(addr, value) { return cpu.mmap_write16(addr, value); },
- "_mmap_write32": function(addr, value) { return cpu.mmap_write32(addr, value); },
- "_mmap_write128": function(addr, value0, value1, value2, value3) { return cpu.mmap_write128(addr, value0, value1, value2, value3); },
- "_int_log2": function(val) { return v86util.int_log2(val); },
- "_popa16": function() { return cpu.popa16.apply(cpu, arguments); },
- "_popa32": function() { return cpu.popa32.apply(cpu, arguments); },
- "_arpl": function() { return cpu.arpl.apply(cpu, arguments); },
- "_bswap": function() { return cpu.bswap.apply(cpu, arguments); },
- "_lar": function() { return cpu.lar.apply(cpu, arguments); },
- "_lsl": function() { return cpu.lsl.apply(cpu, arguments); },
- "_verw": function() { return cpu.verw.apply(cpu, arguments); },
- "_verr": function() { return cpu.verr.apply(cpu, arguments); },
- "_cpl_changed": function() { return cpu.cpl_changed.apply(cpu, arguments); },
- "_set_cr0": function() { return cpu.set_cr0.apply(cpu, arguments); },
- "_update_cs_size": function() { return cpu.update_cs_size.apply(cpu, arguments); },
- "_cpuid": function() { return cpu.cpuid.apply(cpu, arguments); },
- "_load_ldt": function() { return cpu.load_ldt.apply(cpu, arguments); },
- "_load_tr": function() { return cpu.load_tr.apply(cpu, arguments); },
- "_lss16": function() { return cpu.lss16.apply(cpu, arguments); },
- "_lss32": function() { return cpu.lss32.apply(cpu, arguments); },
- "_enter16": function() { return cpu.enter16.apply(cpu, arguments); },
- "_enter32": function() { return cpu.enter32.apply(cpu, arguments); },
- "_test_privileges_for_io": function() { return cpu.test_privileges_for_io.apply(cpu, arguments); },
- "_convert_f64_to_i32": function(f) {
- // implemented here due to emscripten bug
- if(!(f <= 0x7FFFFFFF && f >= -0x80000000))
- {
- f = 0x80000000 | 0;
- }
- return f | 0;
- },
- "_get_time": Date.now,
- "_coverage_log": (fn_name_offset, num_blocks, visited_block) => {
- coverage_logger.log(fn_name_offset, num_blocks, visited_block);
- },
- // see https://github.com/kripken/emscripten/blob/incoming/src/library.js
- "_atan2": Math.atan2,
- "_sin": Math.sin,
- "_cos": Math.cos,
- "_tan": Math.tan,
- "_trunc": Math.trunc,
- "_fmod": (x, y) => x % y,
- "_llvm_exp2_f64": (x) => Math.pow(2, x),
- "_log": Math.log,
- "_round": Math.round,
- "_ldexp": function(x, exp) {
- return x * Math.pow(2, exp);
- },
- "_llvm_round_f64": function(d) {
- d = +d;
- return d >= +0 ? +Math.floor(d + 0.5) : +Math.ceil(d - 0.5);
- },
- "_llvm_trunc_f64": Math.trunc,
- };
- const wasm_globals = {
- "Infinity": Infinity,
- "NaN": NaN,
- };
- const v86oxide_mem = new WebAssembly.Memory({ "initial": 250 });
- const v86oxide_externs = {
- "memory": v86oxide_mem,
- "log_from_wasm": function(offset, len) {
- const str = v86util.read_sized_string_from_mem(v86oxide_mem, offset, len);
- dbg_log(str, LOG_CPU);
- },
- "abort": function() {
- dbg_assert(false);
- },
- "read8": addr => cpu.read8(addr),
- "read16": addr => cpu.read16(addr),
- "read32": addr => cpu.read32s(addr),
- "tlb_set_has_code": (page, has_code) => cpu.wm.exports["_tlb_set_has_code"](page, has_code),
- "check_tlb_invariants": () => cpu.wm.exports["_check_tlb_invariants"](),
- "codegen_finalize": (wasm_table_index, start, end, first_opcode, state_flags) => cpu.codegen_finalize(wasm_table_index, start, end, first_opcode, state_flags),
- "profiler_stat_increment": (name) => cpu.wm.exports["_profiler_stat_increment"](name),
- };
- let wasm_file = DEBUG ? "v86-debug.wasm" : "v86.wasm";
- let v86oxide_bin = DEBUG ? "v86oxide-debug.wasm" : "v86oxide.wasm";
- if(typeof window === "undefined" && typeof __dirname === "string")
- {
- wasm_file = __dirname + "/" + wasm_file;
- v86oxide_bin = __dirname + "/" + v86oxide_bin;
- }
- else
- {
- wasm_file = "build/" + wasm_file;
- v86oxide_bin = "build/" + v86oxide_bin;
- }
- const v86oxide_exports = [
- // For C:
- "jit_get_entry_pending",
- "jit_get_entry_address",
- "jit_get_entry_length",
- "jit_unused_cache_stat",
- "jit_dirty_cache_single",
- "jit_dirty_cache_small",
- "jit_page_has_code",
- "jit_increase_hotness_and_maybe_compile",
- "jit_find_cache_entry",
- // For JS:
- "jit_empty_cache",
- "codegen_finalize_finished",
- "rust_setup",
- "jit_dirty_cache",
- ];
- v86util.minimal_load_wasm(v86oxide_bin, { "env": v86oxide_externs }, (v86oxide) => {
- for(const fn_name of v86oxide_exports)
- {
- dbg_assert(typeof v86oxide.exports[fn_name] === "function", `Function ${fn_name} not found in v86oxide exports`);
- wasm_shared_funcs[`_${fn_name}`] = v86oxide.exports[fn_name];
- }
- v86oxide.exports["rust_setup"]();
- //XXX: fix indentation break
- v86util.load_wasm(
- wasm_file,
- { "env": wasm_shared_funcs, "global" : wasm_globals },
- options["memory_size"] + GUEST_MEMORY_START,
- WASM_TABLE_SIZE,
- wm => {
- mem = wm.memory.buffer;
- mem8 = new Uint8Array(mem);
- wm.instance.exports["__post_instantiate"]();
- coverage_logger.init(wm);
- emulator = this.v86 = new v86(this.emulator_bus, wm, v86oxide, coverage_logger);
- cpu = emulator.cpu;
- // XXX: Leaving unindented to minimize diff; still a part of the cb to load_wasm!
- 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.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.fda = undefined;
- settings.fdb = undefined;
- if(options["network_relay_url"])
- {
- this.network_adapter = new NetworkAdapter(options["network_relay_url"], adapter_bus);
- settings.enable_ne2k = true;
- }
- if(!options["disable_keyboard"])
- {
- this.keyboard_adapter = new KeyboardAdapter(adapter_bus);
- }
- if(!options["disable_mouse"])
- {
- this.mouse_adapter = new MouseAdapter(adapter_bus, options["screen_container"]);
- }
- if(options["screen_container"])
- {
- this.screen_adapter = new ScreenAdapter(options["screen_container"], adapter_bus);
- }
- else if(options["screen_dummy"])
- {
- this.screen_adapter = new DummyScreenAdapter(adapter_bus);
- }
- if(options["serial_container"])
- {
- this.serial_adapter = new SerialAdapter(options["serial_container"], adapter_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;
- 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.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;
- }
- // Anything coming from the outside world needs to be quoted for
- // Closure Compiler compilation
- file = {
- buffer: file["buffer"],
- async: file["async"],
- url: file["url"],
- size: file["size"],
- };
- if(name === "bios" || name === "vga_bios" ||
- name === "initial_state" || name === "multiboot")
- {
- // Ignore async for these because they must be availabe before boot.
- // This should make result.buffer available after the object is loaded
- file.async = false;
- }
- if(file.buffer instanceof ArrayBuffer)
- {
- var buffer = new 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)
- {
- var buffer = new v86util.AsyncXHRBuffer(file.url, file.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",
- ];
- 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"];
- this.fs9p = new FS(base_url);
- settings.fs9p = this.fs9p;
- if(fs_url)
- {
- console.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_text: 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, new 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_text: f.as_text,
- });
- }
- }.bind(this);
- cont(0);
- function done()
- {
- //if(settings.initial_state)
- //{
- // // avoid large allocation now, memory will be restored later anyway
- // settings.memory_size = 0;
- //}
- this.bus.send("cpu-init", settings);
- setTimeout(function()
- {
- if(settings.fs9p && settings.fs9p_json)
- {
- settings.fs9p.OnJSONLoaded(settings.fs9p_json);
- }
- setTimeout(function()
- {
- 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");
- }.bind(this), 0);
- }.bind(this), 0);
- }
- });
- });
- }
- /**
- * Start emulation. Do nothing if emulator is running already. Can be
- * asynchronous.
- * @export
- */
- V86Starter.prototype.run = function()
- {
- this.bus.send("cpu-run");
- };
- /**
- * Stop emulation. Do nothing if emulator is not running. Can be asynchronous.
- * @export
- */
- V86Starter.prototype.stop = function()
- {
- this.bus.send("cpu-stop");
- };
- /**
- * @ignore
- * @export
- */
- V86Starter.prototype.destroy = function()
- {
- this.keyboard_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 = function(state)
- {
- this.v86.restore_state(state);
- };
- /**
- * Asynchronously save the current state of the emulator. The first argument to
- * the callback is an Error object if something went wrong and is null
- * otherwise.
- *
- * @param {function(Object, ArrayBuffer)} callback
- * @export
- */
- V86Starter.prototype.save_state = function(callback)
- {
- // Might become asynchronous at some point
- setTimeout(function()
- {
- try
- {
- callback(null, this.v86.save_state());
- }
- catch(e)
- {
- callback(e, null);
- }
- }.bind(this), 0);
- };
- /**
- * 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.timestamp_counter[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();
- }
- //this.lock_mouse(elem);
- 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));
- }
- };
- /**
- * Write to a file in the 9p filesystem. Nothing happens if no filesystem has
- * been initialized. First argument to the callback is an error object if
- * something went wrong and null otherwise.
- *
- * @param {string} file
- * @param {Uint8Array} data
- * @param {function(Object)=} callback
- * @export
- */
- V86Starter.prototype.create_file = function(file, data, callback)
- {
- 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)
- {
- fs.CreateBinaryFile(filename, parent_id, data);
- }
- if(callback)
- {
- setTimeout(function()
- {
- if(not_found)
- {
- callback(new FileNotFoundError());
- }
- else
- {
- callback(null);
- }
- }, 0);
- }
- };
- /**
- * Read a file in the 9p filesystem. Nothing happens if no filesystem has been
- * initialized.
- *
- * @param {string} file
- * @param {function(Object, Uint8Array)} callback
- * @export
- */
- V86Starter.prototype.read_file = function(file, callback)
- {
- var fs = this.fs9p;
- if(!fs)
- {
- return;
- }
- var path_infos = fs.SearchPath(file);
- var id = path_infos.id;
- if(id === -1)
- {
- callback(new FileNotFoundError(), null);
- }
- else
- {
- fs.OpenInode(id, undefined);
- fs.AddEvent(
- id,
- function()
- {
- var data = fs.inodedata[id];
- if(data)
- {
- callback(null, data.subarray(0, fs.inodes[id].size));
- }
- else
- {
- callback(new FileNotFoundError(), null);
- }
- }
- );
- }
- };
- /**
- * @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;
- }
|