From 1032ef3ef22246801d82ec71ed7555f59a4ce25b Mon Sep 17 00:00:00 2001 From: copy Date: Fri, 9 Jan 2015 04:49:44 +0100 Subject: [PATCH] New publicly usable interface called V86Starter, refactor browser/main.js using it --- Makefile | 18 +- docs/samples/basic.html | 99 +----- loader.js | 2 +- src/browser/main.js | 670 ++++++++++++++++------------------------ src/browser/starter.js | 474 ++++++++++++++++++++++++++++ src/lib.js | 5 - src/main.js | 3 + 7 files changed, 774 insertions(+), 497 deletions(-) create mode 100644 src/browser/starter.js diff --git a/Makefile b/Makefile index 40aec27e80..d6ed2a4c16 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/docs/samples/basic.html b/docs/samples/basic.html index 8242813aff..dafc87f843 100644 --- a/docs/samples/basic.html +++ b/docs/samples/basic.html @@ -1,96 +1,25 @@ Basic Emulator - - - - - - - - - - diff --git a/loader.js b/loader.js index f59d3f1b1b..ad73dda178 100644 --- a/loader.js +++ b/loader.js @@ -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 diff --git a/src/browser/main.js b/src/browser/main.js index a752ffe89e..0e8e9aeb4e 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -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 line = message + " "; + + if(typeof e.file_index === "number" && e.file_count) { - var per100 = e.loaded / (e.total || info.total) * 100 | 0; + 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"); - - start_emulation({ - settings: settings, - done: function(e) { e.run(); }, - }); - }); + set_title(last_file.name); + } - 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; - - var start = after(1, function(result) - { - 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, - }); - } - } - - set_title(infos.name); $("boot_options").style.display = "none"; - } + set_title(infos.name); - function loaded(infos, settings, buffer) - { - settings.memory_size = infos.memory_size; - settings.vga_memory_size = infos.vga_memory_size; + settings.filesystem = infos.filesystem; - if(infos.async_hda) + if(infos.state) { - settings.hda = new v86util.AsyncXHRBuffer( - infos.async_hda, - 512, - infos.async_hda_size - ); + $("reset").style.display = "none"; + settings.initial_state = { + url: infos.state, + }; } - if(infos.fda) + settings.fda = { + url: infos.fda, + }; + settings.cdrom = { + url: infos.cdrom, + }; + + if(infos.hda) { - settings.fda = new SyncBuffer(buffer); + settings.hda = { + url: infos.hda, + }; } - else if(infos.cdrom) + else if(infos.async_hda) { - settings.cdrom = new SyncBuffer(buffer); + settings.hda = { + url: infos.async_hda, + async: true, + size: infos.async_hda_size, + }; } + 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 + // ); + //} + 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) { - var settings = result.settings; - dbg_assert(settings.bios && settings.vga_bios); - - //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]; + /** @const */ + var MB = 1024 * 1024; - var emulator = new v86(device_bus); + var settings = result.settings; + var memory_size = settings.memory_size; - if(DEBUG) + if(!memory_size) { - debug_start(emulator); - } - - // avoid warnings - settings.fdb = undefined; + memory_size = parseInt($("memory_size").value, 10) * MB; - 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(); + + var now = Date.now(); + var last_ips = stats.cpu.instruction_counter - last_instr_counter; - summed_ips += last_ips + 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; + $("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_vga_mode").textContent = "text"; - $("info_res").textContent = "-"; - $("info_bpp").textContent = "-"; - } - - if(settings.mouse_adapter) - { - $("info_mouse_enabled").textContent = settings.mouse_adapter.enabled ? "Yes" : "No"; + $("info_hda").style.display = "none"; } - if(devices.hda) + if(stats.cdrom) { - 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_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_hda").style.display = "none"; + $("info_cdrom").style.display = "none"; } - if(devices.cdrom) + if(stats.vga.is_graphical) { - 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_vga_mode").textContent = "Graphical"; + $("info_res").textContent = stats.vga.res_x + "x" + stats.vga.res_y; + $("info_bpp").textContent = stats.vga.bpp; } else { - $("info_cdrom").style.display = "none"; + $("info_vga_mode").textContent = "Text"; + $("info_res").textContent = "-"; + $("info_bpp").textContent = "-"; } - setTimeout(update_other_info, 1000); + 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 @@ } } + })(); diff --git a/src/browser/starter.js b/src/browser/starter.js new file mode 100644 index 0000000000..47d0e2e23c --- /dev/null +++ b/src/browser/starter.js @@ -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.} 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; +} diff --git a/src/lib.js b/src/lib.js index fcdbf5e883..6a8ee3bbc8 100644 --- a/src/lib.js +++ b/src/lib.js @@ -48,11 +48,6 @@ function h(n, len) } } -if(typeof window === "object") -{ - window["SyncBuffer"] = SyncBuffer; -} - /** * Synchronous access to ArrayBuffer * @constructor diff --git a/src/main.js b/src/main.js index 0e13a6e0c3..d22262338c 100644 --- a/src/main.js +++ b/src/main.js @@ -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); };