| "use strict"; |
| |
| |
| (function() |
| { |
| function log(data) |
| { |
| var log_element = document.getElementById("log"); |
| |
| log_element.style.display = "block"; |
| log_element.textContent += data + "\n"; |
| log_element.scrollTop = 1e9; |
| } |
| |
| |
| function dump_text(text) |
| { |
| var box = document.createElement("textarea"); |
| |
| box.appendChild(document.createTextNode(text)); |
| document.body.appendChild(box); |
| } |
| |
| function dump_file(ab, name) |
| { |
| var blob = new Blob([ab]), |
| a; |
| |
| a = document.createElement("a"); |
| a["download"] = name; |
| a.href = window.URL.createObjectURL(blob), |
| a.textContent = "Download " + name; |
| a.onclick = function() { a.parentNode.removeChild(a); }; |
| |
| a.dataset["downloadurl"] = ["application/octet-stream", a["download"], a.href].join(":"); |
| |
| $("runtime_infos").appendChild(a); |
| } |
| function set_title(text) |
| { |
| document.title = text + " - Virtual x86" + (DEBUG ? " - debug" : ""); |
| } |
| |
| function time2str(time) |
| { |
| if(time < 60) |
| { |
| return time + "s"; |
| } |
| else if(time < 3600) |
| { |
| return (time / 60 | 0) + "m " + String.pad0(time % 60, 2) + "s"; |
| } |
| else |
| { |
| return (time / 3600 | 0) + "h " + |
| String.pad0((time / 60 | 0) % 60, 2) + "m " + |
| String.pad0(time % 60, 2) + "s"; |
| } |
| } |
| |
| /** @param {?=} progress */ |
| function load_file(filename, done, progress, headers) |
| { |
| var http = new XMLHttpRequest(); |
| |
| http.open("get", filename, true); |
| http.responseType = "arraybuffer"; |
| |
| if(headers) |
| { |
| var header_names = Object.keys(headers); |
| |
| for(var i = 0; i < header_names.length; i++) |
| { |
| var name = header_names[i]; |
| |
| http.setRequestHeader(name, headers[name]); |
| } |
| } |
| |
| http.onload = function(e) |
| { |
| if(http.readyState === 4) |
| { |
| if(http.status !== 200 && http.status !== 206) |
| { |
| log("Loading the image `" + filename + "` failed"); |
| } |
| else if(http.response) |
| { |
| done(http.response); |
| } |
| } |
| }; |
| |
| if(progress) |
| { |
| http.onprogress = function(e) |
| { |
| progress(e); |
| }; |
| } |
| |
| http.send(null); |
| } |
| |
| // A function set on the protoype of asynchronous buffers (such as AsyncXHRBuffer) |
| // Relies on this.load_block and this.block_size |
| // Warning: fn may be called synchronously or asynchronously |
| function async_buffer_get(offset, len, fn) |
| { |
| // TODO: Unaligned read |
| dbg_assert(offset % this.block_size === 0); |
| dbg_assert(len % this.block_size === 0); |
| dbg_assert(len); |
| |
| var block_size = this.block_size, |
| blocks_to_load = len / block_size, |
| data, |
| loaded_count = 0, |
| start_block = offset / block_size; |
| |
| if(blocks_to_load > 1) |
| { |
| // copy blocks in this buffer if there is more than one |
| data = new Uint8Array(len); |
| } |
| |
| for(var i = start_block; i < start_block + blocks_to_load; i++) |
| { |
| this.load_block(i, block_loaded); |
| } |
| |
| function block_loaded(buffer, i) |
| { |
| var block = new Uint8Array(buffer); |
| loaded_count++; |
| |
| if(blocks_to_load === 1) |
| { |
| data = block; |
| } |
| else |
| { |
| data.set(block, (i - start_block) * block_size); |
| } |
| |
| if(loaded_count === blocks_to_load) |
| { |
| fn(data); |
| } |
| } |
| } |
| |
| // Likewise, relies on this.byteLength, this.loaded_blocks and this.block_size |
| function async_buffer_set(offset, data, fn) |
| { |
| dbg_assert(offset + data.length <= this.byteLength); |
| |
| var len = data.length; |
| |
| // TODO: Unaligned write |
| dbg_assert(offset % this.block_size === 0); |
| dbg_assert(len % this.block_size === 0); |
| dbg_assert(len); |
| |
| var start_block = offset / this.block_size; |
| var block_count = len / this.block_size; |
| |
| for(var i = 0; i < block_count; i++) |
| { |
| var block = this.loaded_blocks[start_block + i]; |
| |
| if(block === undefined) |
| { |
| block = this.loaded_blocks[start_block + i] = new ArrayBuffer(this.block_size); |
| } |
| |
| var data_slice = data.subarray(i * this.block_size, (i + 1) * this.block_size); |
| new Uint8Array(block).set(data_slice); |
| |
| dbg_assert(block.byteLength === data_slice.length); |
| } |
| |
| fn(); |
| } |
| |
| /** |
| * Asynchronous access to ArrayBuffer, loading blocks lazily as needed. |
| * This is just a prototype and partly incomplete. |
| * |
| * @constructor |
| * @param {string} filename Name of the file to download parts |
| * from. Replaces %d with the block number (padded) |
| */ |
| function AsyncXHRBuffer(filename, block_size, size) |
| { |
| this.block_size = block_size; |
| this.block_count = size / block_size; |
| dbg_assert(this.block_count === (this.block_count | 0)); |
| |
| this.loaded_blocks = []; |
| for(var i = 0; i < this.block_count; i++) |
| { |
| this.loaded_blocks[i] = undefined; |
| } |
| |
| this.byteLength = size; |
| |
| // can be called to preload a block into the cache |
| this.load_block = function(i, fn) |
| { |
| var cached_block = this.loaded_blocks[i]; |
| |
| if(cached_block === undefined) |
| { |
| var me = this; |
| |
| // use Range: bytes=... to load slices of a file |
| var range_start = i * block_size, |
| range_end = range_start + block_size - 1; |
| |
| load_file(filename, |
| function(buffer) |
| { |
| dbg_assert(buffer.byteLength === block_size); |
| |
| me.loaded_blocks[i] = buffer; |
| fn(buffer, i); |
| }, |
| null, |
| { |
| Range: "bytes=" + range_start + "-" + range_end, |
| } |
| ); |
| } |
| else |
| { |
| fn(cached_block, i); |
| } |
| }; |
| |
| this.get_buffer = function(fn) |
| { |
| // We must download all parts, unlikely a good idea for big files |
| }; |
| } |
| AsyncXHRBuffer.prototype.get = async_buffer_get; |
| AsyncXHRBuffer.prototype.set = async_buffer_set; |
| |
| /** |
| * Synchronous access to File, loading blocks from the input type=file |
| * The whole file is loaded into memory during initialisation |
| * |
| * @constructor |
| */ |
| function SyncFileBuffer(file) |
| { |
| var PART_SIZE = 4 << 20, |
| ready = false, |
| me = this; |
| |
| this.byteLength = file.size; |
| |
| if(file.size > (1 << 30)) |
| { |
| dbg_log("Warning: Allocating buffer of " + (file.size >> 20) + " MB ..."); |
| } |
| |
| var buffer = new ArrayBuffer(file.size), |
| pointer = 0, |
| filereader = new FileReader(); |
| |
| this.load = function() |
| { |
| // Here: Read all parts sequentially |
| // Other option: Read all parts in parallel |
| filereader.onload = function(e) |
| { |
| new Uint8Array(buffer, pointer).set(new Uint8Array(e.target.result)); |
| pointer += PART_SIZE; |
| //dbg_log(PART_SIZE + " bytes of file read"); |
| next(); |
| }; |
| |
| next(); |
| |
| function next() |
| { |
| if(me.onprogress) |
| { |
| me.onprogress({ |
| loaded: pointer, |
| total: file.size, |
| lengthComputable: true, |
| }); |
| } |
| |
| if(pointer < file.size) |
| { |
| filereader.readAsArrayBuffer(file.slice(pointer, Math.min(pointer + PART_SIZE, file.size))); |
| } |
| else |
| { |
| ready = true; |
| |
| if(me.onload) |
| { |
| me.onload({ |
| |
| }); |
| } |
| } |
| } |
| } |
| |
| this.get = function(offset, len, fn) |
| { |
| if(ready) |
| { |
| dbg_assert(offset + len <= buffer.byteLength); |
| |
| fn(new Uint8Array(buffer, offset, len)); |
| } |
| else |
| { |
| throw "SyncFileBuffer: Wait for ready"; |
| } |
| }; |
| |
| this.get_buffer = function(fn) |
| { |
| if(ready) |
| { |
| fn(buffer); |
| } |
| else |
| { |
| throw "SyncFileBuffer: Wait for ready"; |
| } |
| }; |
| |
| /** @param data {Uint8Array] */ |
| this.set = function(offset, data, fn) |
| { |
| if(ready) |
| { |
| dbg_assert(offset + data.byteLength <= buffer.byteLength); |
| |
| new Uint8Array(buffer, offset, data.byteLength).set(data); |
| fn(); |
| } |
| else |
| { |
| throw "SyncFileBuffer: Wait for ready"; |
| } |
| }; |
| } |
| |
| /** |
| * Asynchronous access to File, loading blocks from the input type=file |
| * |
| * @constructor |
| */ |
| function AsyncFileBuffer(file) |
| { |
| var BLOCK_SHIFT = 9, |
| BLOCK_SIZE = 1 << BLOCK_SHIFT; |
| |
| this.file = file; |
| this.byteLength = file.size; |
| |
| this.block_count = file.size >> BLOCK_SHIFT; |
| this.block_size = BLOCK_SIZE; |
| |
| this.loaded_blocks = []; |
| for(var i = 0; i < this.block_count; i++) |
| { |
| this.loaded_blocks[i] = undefined; |
| } |
| |
| this.load_block = function(i, fn) |
| { |
| var cached_block = this.loaded_blocks[i]; |
| |
| if(cached_block === undefined) |
| { |
| var fr = new FileReader(); |
| var me = this; |
| |
| fr.onload = function(e) |
| { |
| var buffer = e.target.result; |
| |
| me.loaded_blocks[i] = buffer; |
| fn(buffer, i); |
| }; |
| |
| fr.readAsArrayBuffer(file.slice(i * this.block_size, (i + 1) * this.block_size)); |
| } |
| else |
| { |
| fn(cached_block, i); |
| } |
| }; |
| |
| this.get_buffer = function(fn) |
| { |
| }; |
| |
| this.load = function() |
| { |
| this.onload && this.onload({}); |
| }; |
| } |
| AsyncFileBuffer.prototype.get = function(offset, len, fn) |
| { |
| dbg_assert(offset % this.block_size === 0); |
| dbg_assert(len % this.block_size === 0); |
| dbg_assert(len); |
| |
| var fr = new FileReader(); |
| var me = this; |
| |
| fr.onload = function(e) |
| { |
| var buffer = e.target.result; |
| |
| //me.loaded_blocks[i] = buffer; |
| fn(new Uint8Array(buffer)); |
| }; |
| |
| fr.readAsArrayBuffer(this.file.slice(offset, offset + len)); |
| } |
| //AsyncFileBuffer.prototype.get = async_buffer_get; |
| AsyncFileBuffer.prototype.set = async_buffer_set; |
| |
| function lock_mouse(elem) |
| { |
| var fn = elem["requestPointerLock"] || |
| elem["mozRequestPointerLock"] || |
| elem["webkitRequestPointerLock"]; |
| |
| if(fn) |
| { |
| fn.call(elem); |
| } |
| } |
| |
| function chr_repeat(chr, count) |
| { |
| var result = ""; |
| |
| while(count-- > 0) |
| { |
| result += chr; |
| } |
| |
| return result; |
| } |
| |
| |
| function show_progress(info, e) |
| { |
| var el = $("loading"); |
| el.style.display = "block"; |
| |
| if(e.lengthComputable || (info.total && typeof e.loaded === "number")) |
| { |
| var per100 = e.loaded / (e.total || info.total) * 100 | 0; |
| |
| per100 = Math.min(100, Math.max(0, per100)); |
| |
| el.textContent = info.msg + " " + per100 + "% [" + |
| chr_repeat("#", per100 >> 1) + |
| chr_repeat(" ", 50 - (per100 >> 1)) + "]"; |
| } |
| else |
| { |
| if(!info.ticks) |
| info.ticks = 0; |
| |
| el.textContent = info.msg + " " + chr_repeat(".", info.ticks++ % 50); |
| } |
| } |
| |
| function $(id) |
| { |
| if(!document.getElementById(id)) |
| console.log("Element with id `" + id + "` not found"); |
| |
| return document.getElementById(id); |
| } |
| |
| |
| |
| window.onload = function() |
| { |
| if(!("responseType" in new XMLHttpRequest)) |
| { |
| log("Your browser is not supported because it doesn't have XMLHttpRequest.responseType"); |
| return; |
| } |
| |
| var settings = { |
| load_devices: true |
| }; |
| |
| function load_local(file, type, cont) |
| { |
| 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 SyncFileBuffer(file); |
| loader.onprogress = show_progress.bind(this, { msg: "Loading disk image into memory" }); |
| } |
| else |
| { |
| var loader = new 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; |
| } |
| cont(); |
| } |
| |
| 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; |
| |
| if(DEBUG) |
| { |
| biosfile = "seabios-debug.bin"; |
| } |
| else |
| { |
| biosfile = "seabios.bin"; |
| } |
| |
| load_file("bios/" + biosfile, function(img) |
| { |
| settings.bios = img; |
| }); |
| |
| //load_file("bios/vgabios.bin", function(img) |
| load_file("bios/bochs-vgabios-0.7a.bin", function(img) |
| { |
| settings.vga_bios = img; |
| }); |
| |
| $("start_freedos").onclick = function() |
| { |
| load_file("images/freedos722.img", function(buffer) |
| { |
| settings.fda = new SyncBuffer(buffer); |
| set_title("FreeDOS"); |
| init(settings); |
| }, show_progress.bind(this, { msg: "Downloading image", total: 737280 })); |
| |
| $("start_freedos").blur(); |
| $("boot_options").style.display = "none"; |
| }; |
| |
| $("start_win101").onclick = function() |
| { |
| load_file("images/windows101.img", function(buffer) |
| { |
| settings.fda = new SyncBuffer(buffer); |
| set_title("Windows"); |
| init(settings); |
| }, show_progress.bind(this, { msg: "Downloading image", total: 1474560 })); |
| |
| $("start_win101").blur(); |
| $("boot_options").style.display = "none"; |
| }; |
| |
| |
| $("start_linux").onclick = function() |
| { |
| load_file("images/linux.iso", function(buffer) |
| { |
| settings.cdrom = new SyncBuffer(buffer); |
| set_title("Linux"); |
| init(settings); |
| }, show_progress.bind(this, { msg: "Downloading image", total: 5632000 })); |
| |
| $("start_linux").blur(); |
| $("boot_options").style.display = "none"; |
| }; |
| |
| $("start_koli").onclick = function() |
| { |
| load_file("images/kolibri.img", function(buffer) |
| { |
| settings.fda = new SyncBuffer(buffer); |
| set_title("KolibriOS"); |
| init(settings); |
| }, show_progress.bind(this, { msg: "Downloading image", total: 1474560 })); |
| |
| $("start_koli").blur(); |
| $("boot_options").style.display = "none"; |
| }; |
| |
| $("start_bsd").onclick = function() |
| { |
| load_file("images/openbsd.img", function(buffer) |
| { |
| settings.fda = new SyncBuffer(buffer); |
| set_title("OpenBSD"); |
| init(settings); |
| }, show_progress.bind(this, { msg: "Downloading image", total: 1474560 })); |
| |
| $("start_bsd").blur(); |
| $("boot_options").style.display = "none"; |
| }; |
| |
| $("start_sol").onclick = function() |
| { |
| load_file("images/os8.dsk", function(buffer) |
| { |
| settings.fda = new SyncBuffer(buffer); |
| set_title("Sol OS"); |
| init(settings); |
| }, show_progress.bind(this, { msg: "Downloading image", total: 1474560 })); |
| |
| $("start_sol").blur(); |
| $("boot_options").style.display = "none"; |
| }; |
| |
| $("start_emulation").onclick = function() |
| { |
| $("boot_options").style.display = "none"; |
| |
| var images = []; |
| |
| if($("floppy_image").files.length) |
| { |
| images.push({ |
| file: $("floppy_image").files[0], |
| type: "floppy", |
| }); |
| } |
| |
| if($("cd_image").files.length) |
| { |
| images.push({ |
| file: $("cd_image").files[0], |
| type: "cdrom", |
| }); |
| } |
| |
| if($("hd_image").files.length) |
| { |
| images.push({ |
| file: $("hd_image").files[0], |
| type: "hd", |
| }); |
| } |
| |
| function cont() |
| { |
| if(images.length === 0) |
| { |
| init(settings); |
| } |
| else |
| { |
| var obj = images.pop(); |
| |
| load_local(obj.file, obj.type, cont); |
| } |
| } |
| |
| cont(); |
| }; |
| |
| if(DEBUG) |
| { |
| $("start_test").onclick = function() |
| { |
| //settings.cdrom = new AsyncXHRBuffer("images/linux.iso", 2048, 5632000); |
| //settings.fda = new AsyncXHRBuffer("images/kolibri.img", 512, 1440 * 1024); |
| //settings.fda = new AsyncXHRBuffer("images/freedos722.img", 512, 720 * 1024); |
| //settings.hda = new AsyncXHRBuffer("images/arch.img", 512, 8589934592); |
| settings.cdrom = new AsyncXHRBuffer("https://dl.dropboxusercontent.com/u/61029208/linux.iso", 2048, 6547456); |
| init(settings); |
| } |
| |
| var log_levels = document.getElementById("log_levels"), |
| count = 0, |
| mask; |
| |
| for(var i in dbg_names) |
| { |
| mask = +i; |
| |
| if(mask == 1) |
| continue; |
| |
| var name = dbg_names[mask].toLowerCase(), |
| input = document.createElement("input"), |
| label = document.createElement("label") |
| |
| input.type = "checkbox"; |
| |
| label.htmlFor = input.id = "log_" + name; |
| |
| if(LOG_LEVEL & mask) |
| { |
| input.checked = true; |
| } |
| input.mask = mask; |
| |
| label.appendChild(input); |
| label.appendChild(document.createTextNode(name + " ")); |
| log_levels.appendChild(label); |
| } |
| |
| log_levels.onchange = function(e) |
| { |
| var target = e.target, |
| mask = target.mask; |
| |
| if(target.checked) |
| { |
| LOG_LEVEL |= mask; |
| } |
| else |
| { |
| LOG_LEVEL &= ~mask; |
| } |
| }; |
| } |
| }; |
| |
| // works in firefox and chromium |
| if(document.readyState === "complete") |
| { |
| window.onload(); |
| } |
| |
| |
| // load_external("https://dl.dropbox.com/example/freedos.img.js"); |
| function load_external(url) |
| { |
| window["loaded"] = function(bin_image) |
| { |
| var buffer = new ArrayBuffer(bin_image.length), |
| buffer_array = new Uint8Array(buffer); |
| |
| for(var i = 0; i < bin_image.length; i++) |
| { |
| buffer_array[i] = bin_image.charCodeAt(i); |
| } |
| |
| window["loaded"] = function() { |
| dbg_log("load_external: result loaded twice ?"); |
| }; |
| }; |
| |
| var script = document.createElement("script"); |
| script.src = url; |
| |
| document.body.appendChild(script); |
| } |
| |
| |
| function init(settings) |
| { |
| if(!settings.bios || !settings.vga_bios) |
| { |
| log("The BIOS has not been loaded - reload the page to try again."); |
| return; |
| } |
| |
| var have_serial = true; |
| |
| var cpu = new v86(), |
| screen_adapter = new ScreenAdapter(); |
| |
| $("boot_options").style.display = "none"; |
| $("loading").style.display = "none"; |
| $("runtime_options").style.display = "block"; |
| $("runtime_infos").style.display = "block"; |
| document.getElementsByClassName("phone_keyboard")[0].style.display = "block"; |
| |
| if($("news")) |
| { |
| $("news").style.display = "none"; |
| } |
| |
| if(DEBUG) |
| { |
| $("step").onclick = function() |
| { |
| cpu.debug.step(); |
| } |
| |
| $("run_until").onclick = function() |
| { |
| cpu.debug.run_until(); |
| }; |
| |
| $("debugger").onclick = function() |
| { |
| cpu.debug.debugger(); |
| }; |
| |
| $("dump_gdt").onclick = function() |
| { |
| cpu.debug.dump_gdt_ldt(); |
| }; |
| |
| $("dump_idt").onclick = function() |
| { |
| cpu.debug.dump_idt(); |
| }; |
| |
| $("dump_regs").onclick = function() |
| { |
| cpu.debug.dump_regs(); |
| }; |
| |
| $("dump_pt").onclick = function() |
| { |
| cpu.debug.dump_page_directory(); |
| }; |
| |
| $("dump_instructions").onclick = function() |
| { |
| cpu.debug.dump_instructions(); |
| }; |
| |
| $("memory_dump").onclick = function() |
| { |
| dump_file(cpu.debug.get_memory_dump(), "memory.bin"); |
| }; |
| } |
| |
| var running = true; |
| |
| $("run").onclick = function() |
| { |
| if(running) |
| { |
| running_time += Date.now() - last_tick; |
| $("run").value = "Run"; |
| cpu.stop(); |
| } |
| else |
| { |
| $("run").value = "Pause"; |
| cpu.run(); |
| last_tick = Date.now(); |
| } |
| |
| running = !running; |
| $("run").blur(); |
| }; |
| |
| var time = $("running_time"), |
| ips = $("speed"), |
| avg_ips = $("avg_speed"), |
| last_tick = Date.now(), |
| running_time = 0, |
| summed_ips = 0, |
| last_instr_counter = 0; |
| |
| function update_info() |
| { |
| if(!running) |
| { |
| return; |
| } |
| |
| var now = Date.now(), |
| last_ips = (cpu.timestamp_counter - last_instr_counter) / 1000 | 0; |
| |
| 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; |
| time.textContent = time2str(running_time / 1000 | 0); |
| |
| last_instr_counter = cpu.timestamp_counter; |
| } |
| |
| function update_other_info() |
| { |
| if(!running) |
| { |
| return; |
| } |
| |
| var vga_stats = cpu.devices.vga.stats; |
| |
| if(vga_stats.is_graphical) |
| { |
| $("info_vga_mode").textContent = "graphical"; |
| $("info_res").textContent = vga_stats.res_x + "x" + vga_stats.res_y; |
| $("info_bpp").textContent = vga_stats.bpp; |
| } |
| else |
| { |
| $("info_vga_mode").textContent = "text"; |
| $("info_res").textContent = "-"; |
| $("info_bpp").textContent = "-"; |
| } |
| |
| if(settings.mouse_adapter) |
| { |
| $("info_mouse_enabled").textContent = settings.mouse_adapter.enabled ? "Yes" : "No"; |
| } |
| |
| if(cpu.devices.hda) |
| { |
| var hda_stats = cpu.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; |
| } |
| else |
| { |
| $("info_hda").style.display = "none"; |
| } |
| |
| if(cpu.devices.cdrom) |
| { |
| var cdrom_stats = cpu.devices.cdrom.stats; |
| |
| $("info_cdrom_sectors_read").textContent = cdrom_stats.sectors_read; |
| $("info_cdrom_bytes_read").textContent = cdrom_stats.bytes_read; |
| } |
| else |
| { |
| $("info_cdrom").style.display = "none"; |
| } |
| } |
| |
| setInterval(update_info, 1000); |
| setInterval(update_other_info, 2500); |
| setTimeout(update_other_info, 100); |
| |
| $("reset").onclick = function() |
| { |
| cpu.restart(); |
| $("reset").blur(); |
| }; |
| |
| // writable image types |
| var image_types = ["hda", "hdb", "fda", "fdb"]; |
| |
| for(var i = 0; i < image_types.length; i++) |
| { |
| var elem = $("get_" + image_types[i] + "_image"); |
| |
| if(settings[image_types[i]]) |
| { |
| elem.onclick = (function(type) |
| { |
| var buffer = settings[type]; |
| |
| buffer.get_buffer(function(b) |
| { |
| dump_file(b, type + ".img"); |
| }); |
| |
| this.blur(); |
| |
| }).bind(elem, image_types[i]); |
| } |
| else |
| { |
| elem.style.display = "none"; |
| } |
| } |
| |
| $("ctrlaltdel").onclick = function() |
| { |
| var ps2 = cpu.devices.ps2; |
| |
| 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); |
| |
| $("ctrlaltdel").blur(); |
| }; |
| |
| $("scale").onchange = function() |
| { |
| var n = parseFloat(this.value); |
| |
| if(n || n > 0) |
| { |
| screen_adapter.set_scale(n, n); |
| } |
| else |
| { |
| this.value = "1"; |
| } |
| }; |
| |
| $("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); |
| }; |
| |
| $("screen_container").onclick = function() |
| { |
| // allow text selection |
| if(window.getSelection().isCollapsed) |
| { |
| document.getElementsByClassName("phone_keyboard")[0].focus(); |
| } |
| }; |
| |
| $("take_screenshot").onclick = function() |
| { |
| screen_adapter.make_screenshot(); |
| |
| $("take_screenshot").blur(); |
| }; |
| |
| // avoid warnings |
| settings.fdb = undefined; |
| |
| settings.screen_adapter = screen_adapter; |
| settings.keyboard_adapter = new KeyboardAdapter(); |
| settings.mouse_adapter = new MouseAdapter(); |
| |
| settings.boot_order = parseInt($("boot_order").value, 16); |
| |
| 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 |
| { |
| log("Invalid memory size - ignored."); |
| settings.memory_size = 32 * 1024 * 1024; |
| } |
| |
| 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 |
| { |
| log("Invalid video memory size - ignored."); |
| settings.vga_memory_size = 8 * 1024 * 1024; |
| } |
| |
| if(have_serial) |
| { |
| settings.serial_adapter = new SerialAdapter($("serial")); |
| $("serial").style.display = "block"; |
| } |
| |
| cpu.init(settings); |
| cpu.run(); |
| } |
| |
| })(); |