Clean up wasm loading, remove unused code for emscripten
This commit is contained in:
parent
618062299d
commit
7796d9dcb4
|
@ -18,62 +18,13 @@ var ASYNC_SAFE = false;
|
|||
v86util.AsyncFileBuffer = AsyncFileBuffer;
|
||||
v86util.SyncFileBuffer = SyncFileBuffer;
|
||||
|
||||
/**
|
||||
* Decode a buffer into an unsigned LEB-128 integer
|
||||
* @param {Uint8Array} view Byte-stream of encoded integer
|
||||
* @param {number=} max_bits Maximum number of bits that are represented; see
|
||||
* https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#varuintn
|
||||
*/
|
||||
v86util.decode_leb128_u = function(view, max_bits=256)
|
||||
{
|
||||
dbg_assert(view instanceof Uint8Array);
|
||||
|
||||
const result = {
|
||||
value: 0,
|
||||
next_index: 0,
|
||||
};
|
||||
let shift = 0;
|
||||
const max_bytes = Math.ceil(max_bits / 7);
|
||||
|
||||
while(result.next_index < view.length && result.next_index < max_bytes)
|
||||
{
|
||||
let byte = view[result.next_index++];
|
||||
result.value |= (byte & 127) << shift;
|
||||
if((byte & 128) === 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
shift += 7;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
v86util.decode_dylink = function(module)
|
||||
{
|
||||
// Details on dylink section:
|
||||
// https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
|
||||
const dylink_sections = WebAssembly.Module.customSections(module, "dylink");
|
||||
dbg_assert(dylink_sections && dylink_sections.length === 1);
|
||||
const dylink_section = dylink_sections[0];
|
||||
const view = new Uint8Array(dylink_section);
|
||||
const { value: memory_size, next_index: table_size_start } =
|
||||
v86util.decode_leb128_u(view, 32);
|
||||
const table_size = v86util.decode_leb128_u(view.subarray(table_size_start), 32).value;
|
||||
|
||||
return {
|
||||
memory_size,
|
||||
table_size,
|
||||
};
|
||||
};
|
||||
|
||||
// Reads len characters at offset from Memory object mem as a JS string
|
||||
v86util.read_sized_string_from_mem = function read_sized_string_from_mem(mem, offset, len)
|
||||
{
|
||||
return String.fromCharCode(...new Uint8Array(mem.buffer, offset, len));
|
||||
};
|
||||
|
||||
//XXX: figure out a better way to handle dylink issue than duplicating above function
|
||||
v86util.minimal_load_wasm = function minimal_load_wasm(filename, imports, cb)
|
||||
v86util.load_wasm = function load_wasm(filename, imports, cb)
|
||||
{
|
||||
function load_cb(bytes)
|
||||
{
|
||||
|
@ -92,79 +43,6 @@ var ASYNC_SAFE = false;
|
|||
v86util.load_file(filename, { done: load_cb });
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches, compiles, and instantiates a wasm file
|
||||
* @param {string} filename
|
||||
* @param {Object} imports Object used for WebAssembly module's imports
|
||||
* @param {number} memory_size Bytes of memory the module wants for itself, excluding the space
|
||||
* the dylink section requests.
|
||||
* @param {number} table_size Number of table entries the module wants for itself, excluding
|
||||
* what the dylink section requests.
|
||||
* @param {function(Object)} cb Callback function that receives custom object with instance, memory,
|
||||
* exported functions, imports, and the filename.
|
||||
*/
|
||||
v86util.load_wasm = function load_wasm(filename, imports, memory_size, table_size, cb)
|
||||
{
|
||||
dbg_assert(memory_size > 0);
|
||||
dbg_assert(typeof imports["env"] === "object");
|
||||
|
||||
function load_cb(buffer)
|
||||
{
|
||||
WebAssembly.compile(buffer)
|
||||
.then(module => {
|
||||
const dylink = v86util.decode_dylink(module);
|
||||
let total_mem_pages = Math.ceil(
|
||||
(dylink.memory_size + memory_size) / WASM_PAGE_SIZE
|
||||
);
|
||||
|
||||
// emscripten seems to require a minimum of 256 pages (16 MB)
|
||||
total_mem_pages = Math.max(256, total_mem_pages);
|
||||
|
||||
try
|
||||
{
|
||||
imports["env"]["memory"] = new WebAssembly.Memory({
|
||||
"initial": total_mem_pages,
|
||||
"maximum": total_mem_pages,
|
||||
});
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
console.error(
|
||||
"Failed to allocate WASM memory of %d pages",
|
||||
total_mem_pages
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
imports["env"]["memoryBase"] = memory_size;
|
||||
|
||||
// XXX: Emscripten forces EMULATED_FUNCTION_POINTERS when
|
||||
// using SIDE_MODULE=1, which we use. Newer versions of emscripten add
|
||||
// all exported functions to the WebAssembly.Table, so we need extra space
|
||||
// here
|
||||
const EXTRA_TABLE_SPACE_FOR_EMULATED_FP = 10000;
|
||||
|
||||
imports["env"][WASM_EXPORT_TABLE_NAME] = new WebAssembly.Table({
|
||||
"initial": dylink.table_size + table_size + EXTRA_TABLE_SPACE_FOR_EMULATED_FP,
|
||||
"element": "anyfunc",
|
||||
});
|
||||
imports["env"]["tableBase"] = table_size;
|
||||
|
||||
return WebAssembly.instantiate(module, imports)
|
||||
.then(instance => ({ instance, module }));
|
||||
})
|
||||
.then(({ instance, module }) => {
|
||||
cb({
|
||||
memory: imports["env"]["memory"],
|
||||
exports: instance["exports"],
|
||||
instance,
|
||||
imports,
|
||||
filename,
|
||||
});
|
||||
});
|
||||
}
|
||||
v86util.load_file(filename, { done: load_cb });
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} filename
|
||||
* @param {Object} options
|
||||
|
|
|
@ -89,93 +89,31 @@ function V86Starter(options)
|
|||
|
||||
this.cpu_is_running = false;
|
||||
|
||||
var bus = Bus.create();
|
||||
var adapter_bus = this.bus = bus[0];
|
||||
const bus = Bus.create();
|
||||
const adapter_bus = this.bus = bus[0];
|
||||
this.emulator_bus = bus[1];
|
||||
var emulator;
|
||||
var cpu;
|
||||
var v86oxide;
|
||||
const coverage_logger = new CoverageLogger();
|
||||
|
||||
//if(coverage_logger.ENABLED)
|
||||
//{
|
||||
// this.bus.register("emulator-stopped", function()
|
||||
// {
|
||||
// coverage_logger.dump_to_files();
|
||||
// }, this);
|
||||
//}
|
||||
var cpu;
|
||||
var wasm_memory;
|
||||
|
||||
const wasm_table = new WebAssembly.Table({ element: "anyfunc", "initial": WASM_TABLE_SIZE + WASM_TABLE_OFFSET });
|
||||
|
||||
var wasm_shared_funcs = {
|
||||
"__assert_fail": (condition, file, line, fun) => {
|
||||
const memory = new Uint8Array(v86oxide.exports.memory.buffer);
|
||||
|
||||
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);
|
||||
},
|
||||
const wasm_shared_funcs = {
|
||||
"cpu_exception_hook": (n) => {
|
||||
return this["cpu_exception_hook"] && this["cpu_exception_hook"](n);
|
||||
},
|
||||
"hlt_op": function() { return cpu.hlt_op(); },
|
||||
"abort": function() { dbg_assert(false); },
|
||||
"_dbg_trace": function() { return dbg_trace(); },
|
||||
"logop": function(eip, op) { return cpu.debug.logop(eip, op); },
|
||||
"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(); },
|
||||
"dbg_log": function(string_offset)
|
||||
{
|
||||
},
|
||||
"dbg_log1": function(string_offset)
|
||||
{
|
||||
},
|
||||
"dbg_log2": function(string_offset)
|
||||
{
|
||||
},
|
||||
"dbg_log3": function(string_offset)
|
||||
{
|
||||
},
|
||||
"dbg_log5": function(string_offset)
|
||||
{
|
||||
},
|
||||
"dbg_trace": function()
|
||||
{
|
||||
dbg_trace();
|
||||
},
|
||||
//"printf": function(format_string_offset, stack_top) {
|
||||
// dbg_assert(arguments.length === 2);
|
||||
// dbg_log_wasm(v86oxide.exports.memory.buffer, format_string_offset, stack_top);
|
||||
//},
|
||||
"memcpy_large": function(dest, source, length) {
|
||||
const mem8 = new Uint8Array(v86oxide.exports.memory.buffer);
|
||||
mem8.set(mem8.subarray(source, source + length), dest);
|
||||
return dest;
|
||||
},
|
||||
"memcpy": function(dest, source, length) {
|
||||
const mem8 = new Uint8Array(v86oxide.exports.memory.buffer);
|
||||
mem8.set(mem8.subarray(source, source + length), dest);
|
||||
return dest;
|
||||
},
|
||||
|
||||
"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); },
|
||||
|
@ -193,10 +131,12 @@ function V86Starter(options)
|
|||
"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); },
|
||||
"mmap_write8": function(addr, value) { cpu.mmap_write8(addr, value); },
|
||||
"mmap_write16": function(addr, value) { cpu.mmap_write16(addr, value); },
|
||||
"mmap_write32": function(addr, value) { cpu.mmap_write32(addr, value); },
|
||||
"mmap_write128": function(addr, value0, value1, value2, value3) {
|
||||
cpu.mmap_write128(addr, value0, value1, value2, value3);
|
||||
},
|
||||
|
||||
"arpl": function() { return cpu.arpl.apply(cpu, arguments); },
|
||||
|
||||
|
@ -215,137 +155,69 @@ function V86Starter(options)
|
|||
"enter16": function() { return cpu.enter16.apply(cpu, arguments); },
|
||||
"enter32": function() { return cpu.enter32.apply(cpu, arguments); },
|
||||
|
||||
"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
|
||||
"Math_atan2": Math.atan2,
|
||||
"sin": Math.sin,
|
||||
"cos": Math.cos,
|
||||
"Math_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,
|
||||
// https://github.com/rust-lang/rust/blob/56e46255b39058725d25e74200e03c0c70a0d0d3/src/etc/wasm32-shim.js#L105-L117
|
||||
"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,
|
||||
|
||||
"log_from_wasm": function(offset, len) {
|
||||
const str = v86util.read_sized_string_from_mem(v86oxide.exports.memory, offset, len);
|
||||
const str = v86util.read_sized_string_from_mem(wasm_memory, offset, len);
|
||||
dbg_log(str, LOG_CPU);
|
||||
},
|
||||
"console_log_from_wasm": function(offset, len) {
|
||||
const str = v86util.read_sized_string_from_mem(v86oxide.exports.memory, offset, len);
|
||||
const str = v86util.read_sized_string_from_mem(wasm_memory, offset, len);
|
||||
console.error(str);
|
||||
},
|
||||
"codegen_finalize": (wasm_table_index, start, end, first_opcode, state_flags) => cpu.codegen_finalize(wasm_table_index, start, end, first_opcode, state_flags),
|
||||
"jit_clear_func": (wasm_table_index) => cpu.jit_clear_func(wasm_table_index),
|
||||
"__indirect_function_table": wasm_table,
|
||||
"floor": Math.floor,
|
||||
"ceil": Math.ceil,
|
||||
"fabs": Math.abs,
|
||||
"abs": Math.abs,
|
||||
|
||||
"codegen_finalize": (wasm_table_index, start, end, first_opcode, state_flags) => {
|
||||
cpu.codegen_finalize(wasm_table_index, start, end, first_opcode, state_flags);
|
||||
},
|
||||
"jit_clear_func": (wasm_table_index) => cpu.jit_clear_func(wasm_table_index),
|
||||
"do_task_switch": (selector, has_error_code, error_code) => {
|
||||
cpu.do_task_switch(selector, has_error_code, error_code);
|
||||
},
|
||||
// XXX: Port to Rust
|
||||
"switch_cs_real_mode": (selector) => cpu.switch_cs_real_mode(selector),
|
||||
|
||||
"__indirect_function_table": wasm_table,
|
||||
};
|
||||
|
||||
const wasm_globals = {
|
||||
"Infinity": Infinity,
|
||||
"NaN": NaN,
|
||||
};
|
||||
|
||||
//const v86oxide_mem = new WebAssembly.Memory({ "initial": 250 });
|
||||
//const v86oxide_externs = {
|
||||
// "memory": v86oxide_mem,
|
||||
// "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"](),
|
||||
// "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")
|
||||
if(options["oxide_path"])
|
||||
{
|
||||
v86oxide_bin = options["oxide_path"];
|
||||
}
|
||||
else 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",
|
||||
v86util.load_wasm(
|
||||
v86oxide_bin,
|
||||
{ "env": wasm_shared_funcs },
|
||||
v86oxide => {
|
||||
v86oxide.exports["rust_setup"]();
|
||||
|
||||
// For JS:
|
||||
"jit_empty_cache",
|
||||
"codegen_finalize_finished",
|
||||
"rust_setup",
|
||||
"jit_dirty_cache",
|
||||
];
|
||||
wasm_memory = v86oxide.exports.memory;
|
||||
|
||||
v86util.minimal_load_wasm(v86oxide_bin, { "env": wasm_shared_funcs }, (v86oxide_) => {
|
||||
v86oxide = 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"]();
|
||||
const wm = v86oxide;
|
||||
|
||||
//v86oxide.exports[WASM_EXPORT_TABLE_NAME].grow(WASM_TABLE_SIZE);
|
||||
const emulator = this.v86 = new v86(this.emulator_bus, wm, v86oxide);
|
||||
cpu = emulator.cpu;
|
||||
|
||||
const wm = v86oxide;
|
||||
this.continue_init(emulator, options);
|
||||
});
|
||||
}
|
||||
|
||||
//mem = v86oxide.exports.memory.buffer;
|
||||
//mem8 = new Uint8Array(mem);
|
||||
emulator = this.v86 = new v86(this.emulator_bus, wm, v86oxide, coverage_logger);
|
||||
cpu = emulator.cpu;
|
||||
|
||||
//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!
|
||||
V86Starter.prototype.continue_init = function(emulator, options)
|
||||
{
|
||||
this.bus.register("emulator-stopped", function()
|
||||
{
|
||||
this.cpu_is_running = false;
|
||||
|
@ -376,31 +248,31 @@ function V86Starter(options)
|
|||
|
||||
if(options["network_relay_url"])
|
||||
{
|
||||
this.network_adapter = new NetworkAdapter(options["network_relay_url"], adapter_bus);
|
||||
this.network_adapter = new NetworkAdapter(options["network_relay_url"], this.bus);
|
||||
settings.enable_ne2k = true;
|
||||
}
|
||||
|
||||
if(!options["disable_keyboard"])
|
||||
{
|
||||
this.keyboard_adapter = new KeyboardAdapter(adapter_bus);
|
||||
this.keyboard_adapter = new KeyboardAdapter(this.bus);
|
||||
}
|
||||
if(!options["disable_mouse"])
|
||||
{
|
||||
this.mouse_adapter = new MouseAdapter(adapter_bus, options["screen_container"]);
|
||||
this.mouse_adapter = new MouseAdapter(this.bus, options["screen_container"]);
|
||||
}
|
||||
|
||||
if(options["screen_container"])
|
||||
{
|
||||
this.screen_adapter = new ScreenAdapter(options["screen_container"], adapter_bus);
|
||||
this.screen_adapter = new ScreenAdapter(options["screen_container"], this.bus);
|
||||
}
|
||||
else if(options["screen_dummy"])
|
||||
{
|
||||
this.screen_adapter = new DummyScreenAdapter(adapter_bus);
|
||||
this.screen_adapter = new DummyScreenAdapter(this.bus);
|
||||
}
|
||||
|
||||
if(options["serial_container"])
|
||||
{
|
||||
this.serial_adapter = new SerialAdapter(options["serial_container"], adapter_bus);
|
||||
this.serial_adapter = new SerialAdapter(options["serial_container"], this.bus);
|
||||
}
|
||||
|
||||
// ugly, but required for closure compiler compilation
|
||||
|
@ -691,10 +563,7 @@ function V86Starter(options)
|
|||
}.bind(this), 0);
|
||||
}.bind(this), 0);
|
||||
}
|
||||
|
||||
//});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Start emulation. Do nothing if emulator is running already. Can be
|
||||
|
|
Loading…
Reference in a new issue