diff --git a/Makefile b/Makefile index 4a57b0f7..9e025ef4 100644 --- a/Makefile +++ b/Makefile @@ -89,7 +89,7 @@ CC_FLAGS=\ -s WASM=1 \ -s SIDE_MODULE=1 -CORE_FILES=const.js config.js io.js main.js lib.js coverage.js fpu.js ide.js pci.js floppy.js \ +CORE_FILES=const.js config.js io.js main.js lib.js coverage.js ide.js pci.js floppy.js \ memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js hpet.js acpi.js apic.js ioapic.js \ state.js ne2k.js virtio.js bus.js log.js \ cpu.js debug.js \ diff --git a/loader.js b/loader.js index 54af5b24..7e317df8 100644 --- a/loader.js +++ b/loader.js @@ -6,7 +6,7 @@ var CORE_FILES = "const.js config.js log.js lib.js coverage.js cpu.js debug.js codegen.js " + - "io.js main.js ide.js fpu.js pci.js floppy.js " + + "io.js main.js ide.js pci.js floppy.js " + "memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js acpi.js apic.js ioapic.js hpet.js " + "ne2k.js state.js virtio.js bus.js elf.js"; diff --git a/src/browser/starter.js b/src/browser/starter.js index 02cf0ba6..d193a109 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -187,8 +187,6 @@ function V86Starter(options) "_popa16": function() { return cpu.popa16.apply(cpu, arguments); }, "_popa32": function() { return cpu.popa32.apply(cpu, arguments); }, "_arpl": function() { return cpu.arpl.apply(cpu, arguments); }, - "_getiopl": function() { return cpu.getiopl.apply(cpu, arguments); }, - "_vm86_mode": function() { return cpu.vm86_mode.apply(cpu, arguments); }, "_bswap": function() { return cpu.bswap.apply(cpu, arguments); }, @@ -212,11 +210,11 @@ function V86Starter(options) "_lss32": function() { return cpu.lss32.apply(cpu, arguments); }, "_enter16": function() { return cpu.enter16.apply(cpu, arguments); }, "_enter32": function() { return cpu.enter32.apply(cpu, arguments); }, - "_update_eflags": function() { return cpu.update_eflags.apply(cpu, arguments); }, "_loop": function() { return cpu.loop.apply(cpu, arguments); }, "_loope": function() { return cpu.loope.apply(cpu, arguments); }, "_loopne": function() { return cpu.loopne.apply(cpu, arguments); }, "_jcxz": function() { return cpu.jcxz.apply(cpu, arguments); }, + "_test_privileges_for_io": function() { return cpu.test_privileges_for_io.apply(cpu, arguments); }, "_convert_f64_to_i32": function(f) { diff --git a/src/cpu.js b/src/cpu.js index 4c51d41b..617c3eb6 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -244,8 +244,6 @@ function CPU(bus, wm, codegen, coverage_logger) this.debug_init(); - this.init2(); - //Object.seal(this); } @@ -275,156 +273,51 @@ CPU.prototype.create_jit_imports = function() CPU.prototype.wasm_patch = function(wm) { - this.add = this.wm.exports['_add']; - this.adc = this.wm.exports['_adc']; - this.sub = this.wm.exports['_sub']; - this.sbb = this.wm.exports['_sbb']; - this.inc = this.wm.exports['_inc']; - this.dec = this.wm.exports['_dec']; - this.neg = this.wm.exports['_neg']; - this.mul8 = this.wm.exports['_mul8']; - this.imul8 = this.wm.exports['_imul8']; - this.mul16 = this.wm.exports['_mul16']; - this.imul16 = this.wm.exports['_imul16']; - this.imul_reg16 = this.wm.exports['_imul_reg16']; - this.mul32 = this.wm.exports['_mul32']; - this.imul32 = this.wm.exports['_imul32']; - this.imul_reg32 = this.wm.exports['_imul_reg32']; - this.xadd8 = this.wm.exports['_xadd8']; - this.xadd16 = this.wm.exports['_xadd16']; - this.xadd32 = this.wm.exports['_xadd32']; - this.bcd_daa = this.wm.exports['_bcd_daa']; - this.bcd_das = this.wm.exports['_bcd_das']; - this.bcd_aad = this.wm.exports['_bcd_aad']; - this.bcd_aaa = this.wm.exports['_bcd_aaa']; - this.bcd_aas = this.wm.exports['_bcd_aas']; - this.and = this.wm.exports['_and']; - this.or = this.wm.exports['_or']; - this.xor = this.wm.exports['_xor']; - this.rol8 = this.wm.exports['_rol8']; - this.rol16 = this.wm.exports['_rol16']; - this.rol32 = this.wm.exports['_rol32']; - this.rcl8 = this.wm.exports['_rcl8']; - this.rcl16 = this.wm.exports['_rcl16']; - this.rcl32 = this.wm.exports['_rcl32']; - this.ror8 = this.wm.exports['_ror8']; - this.ror16 = this.wm.exports['_ror16']; - this.ror32 = this.wm.exports['_ror32']; - this.rcr8 = this.wm.exports['_rcr8']; - this.rcr16 = this.wm.exports['_rcr16']; - this.rcr32 = this.wm.exports['_rcr32']; - this.div8 = this.wm.exports['_div8']; - this.idiv8 = this.wm.exports['_idiv8']; - this.div16 = this.wm.exports['_div16']; - this.getcf = this.wm.exports['_getcf']; - this.getaf = this.wm.exports['_getaf']; - this.raise_exception = this.wm.exports['_raise_exception']; - this.raise_exception_with_code = this.wm.exports['_raise_exception_with_code']; - this.trigger_de = this.wm.exports['_trigger_de']; + this.getzf = this.wm.exports['_getzf']; + + this.getiopl = this.wm.exports['_getiopl']; + this.vm86_mode = this.wm.exports['_vm86_mode']; + this.get_eflags = this.wm.exports['_get_eflags']; + this.update_eflags = this.wm.exports['_update_eflags']; + this.trigger_gp = this.wm.exports['_trigger_gp']; - this.div32 = this.wm.exports['_div32']; - this.idiv32 = this.wm.exports['_idiv32']; - this.idiv16 = this.wm.exports['_idiv16']; - - this.shl8 = this.wm.exports['_shl8']; - this.shl16 = this.wm.exports['_shl16']; - this.shl32 = this.wm.exports['_shl32']; - this.shr8 = this.wm.exports['_shr8']; - this.shr16 = this.wm.exports['_shr16']; - this.shr32 = this.wm.exports['_shr32']; - this.sar8 = this.wm.exports['_sar8']; - this.sar16 = this.wm.exports['_sar16']; - this.sar32 = this.wm.exports['_sar32']; - this.shrd16 = this.wm.exports['_shrd16']; - this.shrd32 = this.wm.exports['_shrd32']; - this.shld16 = this.wm.exports['_shld16']; - this.shld32 = this.wm.exports['_shld32']; - - this.bt_reg = this.wm.exports['_bt_reg']; - this.btc_reg = this.wm.exports['_btc_reg']; - this.bts_reg = this.wm.exports['_bts_reg']; - this.btr_reg = this.wm.exports['_btr_reg']; - this.bt_mem = this.wm.exports['_bt_mem']; - this.btc_mem = this.wm.exports['_btc_mem']; - this.btr_mem = this.wm.exports['_btr_mem']; - this.bts_mem = this.wm.exports['_bts_mem']; - this.bsf16 = this.wm.exports['_bsf16']; - this.bsf32 = this.wm.exports['_bsf32']; - this.bsr16 = this.wm.exports['_bsr16']; - this.bsr32 = this.wm.exports['_bsr32']; - this.popcnt = this.wm.exports['_popcnt']; - this.saturate_sw_to_ub = this.wm.exports['_saturate_sw_to_ub']; - this.saturate_sw_to_sb = this.wm.exports['_saturate_sw_to_sb']; - this.saturate_sd_to_sw = this.wm.exports['_saturate_sd_to_sw']; - this.saturate_sd_to_sb = this.wm.exports['_saturate_sd_to_sb']; - this.saturate_sd_to_ub = this.wm.exports['_saturate_sd_to_ub']; - this.saturate_ud_to_ub = this.wm.exports['_saturate_ud_to_ub']; - this.saturate_uw = this.wm.exports['_saturate_uw']; + this.trigger_ud = this.wm.exports['_trigger_ud']; + this.trigger_np = this.wm.exports['_trigger_np']; + this.trigger_ss = this.wm.exports['_trigger_ss']; this.do_many_cycles_unsafe = this.wm.exports['_do_many_cycles_unsafe']; + this.cycle_internal = this.wm.exports['_cycle_internal']; - this.read_imm8 = this.wm.exports['_read_imm8']; - this.read_imm8s = this.wm.exports['_read_imm8s']; - this.read_imm16 = this.wm.exports['_read_imm16']; - this.read_imm32s = this.wm.exports['_read_imm32s']; - this.in_mapped_range = this.wm.exports['_in_mapped_range']; this.read16 = this.wm.exports['_read16']; - this.read_aligned16 = this.wm.exports['_read_aligned16']; this.read32s = this.wm.exports['_read32s']; - this.read_aligned32 = this.wm.exports['_read_aligned32']; this.write8 = this.wm.exports['_write8']; this.write16 = this.wm.exports['_write16']; - this.write_aligned16 = this.wm.exports['_write_aligned16']; this.write32 = this.wm.exports['_write32']; - this.write_aligned32 = this.wm.exports['_write_aligned32']; this.push16 = this.wm.exports['_push16']; this.push32 = this.wm.exports['_push32']; - this.pusha16 = this.wm.exports['_pusha16']; - this.pusha32 = this.wm.exports['_pusha32']; this.pop16 = this.wm.exports['_pop16']; - this.get_stack_reg = this.wm.exports['_get_stack_reg']; + this.pop32s = this.wm.exports['_pop32s']; + this.set_stack_reg = this.wm.exports['_set_stack_reg']; this.get_reg_asize = this.wm.exports['_get_reg_asize']; this.set_ecx_asize = this.wm.exports['_set_ecx_asize']; this.add_reg_asize = this.wm.exports['_add_reg_asize']; this.decr_ecx_asize = this.wm.exports['_decr_ecx_asize']; - this.movsb = this.wm.exports['_movsb']; - this.movsw = this.wm.exports['_movsw']; - this.movsd = this.wm.exports['_movsd']; - this.cmpsb = this.wm.exports['_cmpsb']; - this.cmpsw = this.wm.exports['_cmpsw']; - this.cmpsd = this.wm.exports['_cmpsd']; - this.stosb = this.wm.exports['_stosb']; - this.stosw = this.wm.exports['_stosw']; - this.stosd = this.wm.exports['_stosd']; - this.lodsb = this.wm.exports['_lodsb']; - this.lodsw = this.wm.exports['_lodsw']; - this.lodsd = this.wm.exports['_lodsd']; - this.scasb = this.wm.exports['_scasb']; - this.scasw = this.wm.exports['_scasw']; - this.scasd = this.wm.exports['_scasd']; - this.insb = this.wm.exports['_insb']; - this.insw = this.wm.exports['_insw']; - this.insd = this.wm.exports['_insd']; - this.outsb = this.wm.exports['_outsb']; - this.outsw = this.wm.exports['_outsw']; - this.outsd = this.wm.exports['_outsd']; - this.getzf = this.wm.exports['_getzf']; - this.getsf = this.wm.exports['_getsf']; - this.getof = this.wm.exports['_getof']; - this.pop32s = this.wm.exports['_pop32s']; - this.test_o = this.wm.exports['_test_o']; - this.test_b = this.wm.exports['_test_b']; - this.test_z = this.wm.exports['_test_z']; - this.test_s = this.wm.exports['_test_s']; - this.test_p = this.wm.exports['_test_p']; - this.test_l = this.wm.exports['_test_l']; - this.test_be = this.wm.exports['_test_be']; - this.test_le = this.wm.exports['_test_le']; + this.translate_address_read = this.wm.exports["_translate_address_read"]; + this.translate_address_system_read = this.wm.exports["_translate_address_system_read"]; + this.translate_address_system_write = this.wm.exports["_translate_address_system_write"]; - this.fxsave = this.wm.exports['_fxsave']; - this.fxrstor = this.wm.exports['_fxrstor']; + this.get_seg = this.wm.exports["_get_seg"]; + this.adjust_stack_reg = this.wm.exports["_adjust_stack_reg"]; + this.get_real_eip = this.wm.exports["_get_real_eip"]; + this.get_stack_pointer = this.wm.exports["_get_stack_pointer"]; + + this.writable_or_pagefault = this.wm.exports["_writable_or_pagefault"]; + this.safe_write32 = this.wm.exports["_safe_write32"]; + this.safe_read32s = this.wm.exports["_safe_read32s"]; + this.safe_write16 = this.wm.exports["_safe_write16"]; + this.safe_read16 = this.wm.exports["_safe_read16"]; }; CPU.prototype.jit_clear_func = function(index) @@ -643,7 +536,7 @@ CPU.prototype.exception_cleanup = function(e) this.page_fault[0] = 0; // restore state from prefixes - this.clear_prefixes(); + this.prefixes[0] = 0; } else { @@ -1296,15 +1189,17 @@ CPU.prototype.do_many_cycles = function() this.wm.exports["_profiler_end"](P_RUN_INTERPRETED); }; -CPU.prototype.do_many_cycles_unsafe = function() +/** @export */ +CPU.prototype.cycle = function() { - dbg_assert(false); - // inner loop: - // runs only cycles - for(var k = LOOP_COUNTER; k--;) - { + try { + // XXX: May do several cycles this.cycle_internal(); } + catch(e) + { + this.exception_cleanup(e); + } }; // Some functions must not be inlined, because then more code is in the @@ -1348,59 +1243,6 @@ if(PROFILING) }; } -/** - * execute a single instruction cycle on the cpu - * this includes reading all prefixes and the whole instruction - */ -CPU.prototype.cycle_internal = function() -{ - this.previous_ip[0] = this.instruction_pointer[0]; - - this.timestamp_counter[0]++; - - if(PROFILING) - { - var start = performance.now(); - } - - var opcode = this.read_imm8(); - //this.translate_address_read(this.instruction_pointer + 15|0) - - if(DEBUG) - { - this.debug.logop(this.instruction_pointer[0] - 1 >>> 0, opcode); - } - - // call the instruction - this.table[opcode](this); - - if(PROFILING) - { - var end = performance.now(); - instruction_total[opcode] += end - start; - instruction_count[opcode]++; - } - - if(this.flags[0] & flag_trap) - { - // TODO - dbg_log("Trap flag: Ignored", LOG_CPU); - } -}; - -/** @export */ -CPU.prototype.cycle = function() -{ - try - { - this.cycle_internal(); - } - catch(e) - { - this.exception_cleanup(e); - } -}; - var seen_code = {}; CPU.prototype.codegen_finalize = function(cache_index, virtual_start, start, end) @@ -1519,11 +1361,6 @@ CPU.prototype.run_hardware_timers = function(now) } }; -CPU.prototype.clear_prefixes = function() -{ - this.prefixes[0] = 0; -}; - CPU.prototype.set_cr0 = function(cr0) { //dbg_log("cr0 = " + h(this.cr[0] >>> 0), LOG_CPU); @@ -1607,89 +1444,6 @@ CPU.prototype.cpl_changed = function() this.last_virt_esp[0] = -1; }; -CPU.prototype.read_imm8 = function() -{ - if((this.instruction_pointer[0] & ~0xFFF) ^ this.last_virt_eip[0]) - { - this.eip_phys[0] = this.translate_address_read(this.instruction_pointer[0]) ^ this.instruction_pointer[0]; - this.last_virt_eip[0] = this.instruction_pointer[0] & ~0xFFF; - } - - var data8 = this.read8(this.eip_phys[0] ^ this.instruction_pointer[0]); - this.instruction_pointer[0] = this.instruction_pointer[0] + 1 | 0; - - return data8; -}; - -CPU.prototype.read_imm8s = function() -{ - return this.read_imm8() << 24 >> 24; -}; - -CPU.prototype.read_imm16 = function() -{ - // Two checks in one comparison: - // 1. Did the high 20 bits of eip change - // or 2. Are the low 12 bits of eip 0xFFF (and this read crosses a page boundary) - if(((this.instruction_pointer[0] ^ this.last_virt_eip[0]) >>> 0) > 0xFFE) - { - return this.read_imm8() | this.read_imm8() << 8; - } - - var data16 = this.read16(this.eip_phys[0] ^ this.instruction_pointer[0]); - this.instruction_pointer[0] = this.instruction_pointer[0] + 2 | 0; - - return data16; -}; - -CPU.prototype.read_imm32s = function() -{ - // Analogue to the above comment - if(((this.instruction_pointer[0] ^ this.last_virt_eip[0]) >>> 0) > 0xFFC) - { - return this.read_imm16() | this.read_imm16() << 16; - } - - var data32 = this.read32s(this.eip_phys[0] ^ this.instruction_pointer[0]); - this.instruction_pointer[0] = this.instruction_pointer[0] + 4 | 0; - - return data32; -}; - -/* - * Create an internal data unit to represent 64 bits of data - */ -CPU.prototype.create_atom64s = function(low, high) -{ - let data = new Int32Array(2); - data[0] = low; - data[1] = high; - return data; -}; - -CPU.prototype.create_atom128s = function(d0, d1, d2, d3) -{ - let data = new Int32Array(4); - data[0] = d0; - data[1] = d1; - data[2] = d2; - data[3] = d3; - return data; -}; - -CPU.prototype.read_op0F = CPU.prototype.read_imm8; -CPU.prototype.read_sib = CPU.prototype.read_imm8; -CPU.prototype.read_op8 = CPU.prototype.read_imm8; -CPU.prototype.read_op8s = CPU.prototype.read_imm8s; -CPU.prototype.read_op16 = CPU.prototype.read_imm16; -CPU.prototype.read_op32s = CPU.prototype.read_imm32s; -CPU.prototype.read_disp8 = CPU.prototype.read_imm8; -CPU.prototype.read_disp8s = CPU.prototype.read_imm8s; -CPU.prototype.read_disp16 = CPU.prototype.read_imm16; -CPU.prototype.read_disp32s = CPU.prototype.read_imm32s; - -CPU.prototype.init2 = function () {}; - CPU.prototype.after_jump = function () { // May be called through JS imports in the WASM module, such as loop or handle_irqs (through popf, sti) @@ -1710,336 +1464,6 @@ CPU.prototype.jit_empty_cache = function() this.wm.exports["_jit_empty_cache"](); }; -// read word from a page boundary, given 2 physical addresses -CPU.prototype.virt_boundary_read16 = function(low, high) -{ - dbg_assert((low & 0xFFF) === 0xFFF); - dbg_assert((high & 0xFFF) === 0); - - return this.read8(low) | this.read8(high) << 8; -}; - -// read doubleword from a page boundary, given 2 addresses -CPU.prototype.virt_boundary_read32s = function(low, high) -{ - dbg_assert((low & 0xFFF) >= 0xFFD); - dbg_assert((high - 3 & 0xFFF) === (low & 0xFFF)); - - var mid; - - if(low & 1) - { - if(low & 2) - { - // 0xFFF - mid = this.read_aligned16(high - 2 >> 1); - } - else - { - // 0xFFD - mid = this.read_aligned16(low + 1 >> 1); - } - } - else - { - // 0xFFE - mid = this.virt_boundary_read16(low + 1 | 0, high - 1 | 0); - } - - return this.read8(low) | mid << 8 | this.read8(high) << 24; -}; - -CPU.prototype.virt_boundary_write16 = function(low, high, value) -{ - dbg_assert((low & 0xFFF) === 0xFFF); - dbg_assert((high & 0xFFF) === 0); - - this.write8(low, value); - this.write8(high, value >> 8); -}; - -CPU.prototype.virt_boundary_write32 = function(low, high, value) -{ - dbg_assert((low & 0xFFF) >= 0xFFD); - dbg_assert((high - 3 & 0xFFF) === (low & 0xFFF)); - - this.write8(low, value); - this.write8(high, value >> 24); - - if(low & 1) - { - if(low & 2) - { - // 0xFFF - this.write8(high - 2, value >> 8); - this.write8(high - 1, value >> 16); - } - else - { - // 0xFFD - this.write8(low + 1 | 0, value >> 8); - this.write8(low + 2 | 0, value >> 16); - } - } - else - { - // 0xFFE - this.write8(low + 1 | 0, value >> 8); - this.write8(high - 1, value >> 16); - } -}; - -// safe_read, safe_write -// read or write byte, word or dword to the given *virtual* address, -// and be safe on page boundaries - -CPU.prototype.safe_read8 = function(addr) -{ - dbg_assert(addr < 0x80000000); - return this.read8(this.translate_address_read(addr)); -}; - -CPU.prototype.safe_read16 = function(addr) -{ - if(this.paging[0] && (addr & 0xFFF) === 0xFFF) - { - return this.safe_read8(addr) | this.safe_read8(addr + 1 | 0) << 8; - } - else - { - return this.read16(this.translate_address_read(addr)); - } -}; - -CPU.prototype.safe_read32s = function(addr) -{ - if(this.paging[0] && (addr & 0xFFF) >= 0xFFD) - { - return this.safe_read16(addr) | this.safe_read16(addr + 2 | 0) << 16; - } - else - { - return this.read32s(this.translate_address_read(addr)); - } -}; - -CPU.prototype.safe_read64s = function(addr) -{ - let data = this.create_atom64s(0, 0); - if(this.paging && (addr & 0xFFF) >= 0xFF9) - { - data[0] = this.safe_read32s(addr); - data[1] = this.safe_read32s(addr + 4 | 0); - } - else - { - data[0] = this.read32s(this.translate_address_read(addr)); - data[1] = this.read32s(this.translate_address_read(addr + 4 | 0)); - } - return data; -}; - -CPU.prototype.safe_read128s_aligned = function(addr) -{ - dbg_assert((addr & 0xF) === 0); - let phys = this.translate_address_read(addr); - return this.create_atom128s( - this.read32s(phys), - this.read32s(phys + 4 | 0), - this.read32s(phys + 8 | 0), - this.read32s(phys + 12 | 0) - ); -}; - -CPU.prototype.safe_read128s_unaligned = function(addr) -{ - return this.create_atom128s( - this.safe_read32s(addr), - this.safe_read32s(addr + 4 | 0), - this.safe_read32s(addr + 8 | 0), - this.safe_read32s(addr + 12 | 0) - ); -}; - -CPU.prototype.safe_write8 = function(addr, value) -{ - dbg_assert(addr < 0x80000000); - this.write8(this.translate_address_write(addr), value); -}; - -CPU.prototype.safe_write16 = function(addr, value) -{ - var phys_low = this.translate_address_write(addr); - - if((addr & 0xFFF) === 0xFFF) - { - this.virt_boundary_write16(phys_low, this.translate_address_write(addr + 1 | 0), value); - } - else - { - this.write16(phys_low, value); - } -}; - -CPU.prototype.safe_write32 = function(addr, value) -{ - var phys_low = this.translate_address_write(addr); - - if((addr & 0xFFF) >= 0xFFD) - { - this.virt_boundary_write32(phys_low, this.translate_address_write(addr + 3 & ~3) | (addr + 3) & 3, value); - } - else - { - this.write32(phys_low, value); - } -}; - -CPU.prototype.safe_write64 = function(addr, low, high) -{ - this.writable_or_pagefault(addr, 8); - this.safe_write32(addr, low); - this.safe_write32(addr + 4 | 0, high); -}; - -CPU.prototype.safe_write128 = function(addr, d0, d1, d2, d3) -{ - this.writable_or_pagefault(addr, 16); - this.safe_write32(addr, d0); - this.safe_write32(addr + 4 | 0, d1); - this.safe_write32(addr + 8 | 0, d2); - this.safe_write32(addr + 12 | 0, d3); -}; - - -// read 2 or 4 byte from ip, depending on address size attribute -CPU.prototype.read_moffs = function() -{ - if(this.is_asize_32()) - { - return this.get_seg_prefix(reg_ds) + this.read_op32s() | 0; - } - else - { - return this.get_seg_prefix(reg_ds) + this.read_op16() | 0; - } -}; - -CPU.prototype.getiopl = function() -{ - return this.flags[0] >> 12 & 3; -}; - -CPU.prototype.vm86_mode = function() -{ - return !!(this.flags[0] & flag_vm); -}; - -CPU.prototype.get_eflags = function() -{ - return (this.flags[0] & ~flags_all) | !!this.getcf() | !!this.getpf() << 2 | !!this.getaf() << 4 | - !!this.getzf() << 6 | !!this.getsf() << 7 | !!this.getof() << 11; -}; - -/** - * Update the flags register depending on iopl and cpl - */ -CPU.prototype.update_eflags = function(new_flags) -{ - var dont_update = flag_rf | flag_vm | flag_vip | flag_vif, - clear = ~flag_vip & ~flag_vif & flags_mask; - - if(this.flags[0] & flag_vm) - { - // other case needs to be handled in popf or iret - dbg_assert(this.getiopl() === 3); - - dont_update |= flag_iopl; - - // don't clear vip or vif - clear |= flag_vip | flag_vif; - } - else - { - if(!this.protected_mode[0]) dbg_assert(this.cpl[0] === 0); - - if(this.cpl[0]) - { - // cpl > 0 - // cannot update iopl - dont_update |= flag_iopl; - - if(this.cpl[0] > this.getiopl()) - { - // cpl > iopl - // cannot update interrupt flag - dont_update |= flag_interrupt; - } - } - } - - this.flags[0] = (new_flags ^ ((this.flags[0] ^ new_flags) & dont_update)) & clear | flags_default; - - this.flags_changed[0] = 0; -}; - -CPU.prototype.get_stack_reg = function() -{ - if(this.stack_size_32[0]) - { - return this.reg32s[reg_esp]; - } - else - { - return this.reg16[reg_sp]; - } -}; - -CPU.prototype.set_stack_reg = function(value) -{ - if(this.stack_size_32[0]) - { - this.reg32s[reg_esp] = value; - } - else - { - this.reg16[reg_sp] = value; - } -}; - -CPU.prototype.adjust_stack_reg = function(value) -{ - if(this.stack_size_32[0]) - { - this.reg32s[reg_esp] += value; - } - else - { - this.reg16[reg_sp] += value; - } -}; - -CPU.prototype.get_stack_pointer = function(mod) -{ - if(this.stack_size_32[0]) - { - return this.get_seg(reg_ss) + this.reg32s[reg_esp] + mod | 0; - } - else - { - return this.get_seg(reg_ss) + (this.reg16[reg_sp] + mod & 0xFFFF) | 0; - } -}; - -/* - * returns the "real" instruction pointer, - * without segment offset - */ -CPU.prototype.get_real_eip = function() -{ - return this.instruction_pointer[0] - this.get_seg(reg_cs) | 0; -}; - CPU.prototype.call_interrupt_vector = function(interrupt_nr, is_software_int, has_error_code, error_code) { //dbg_log("int " + h(interrupt_nr, 2) + " (" + (is_software_int ? "soft" : "hard") + "ware)", LOG_CPU); @@ -3407,104 +2831,6 @@ CPU.prototype.hlt_op = function() } }; -// assumes ip to point to the byte before the next instruction -CPU.prototype.raise_exception = function(interrupt_nr) -{ - //if(DEBUG && interrupt_nr !== 7) - //{ - // // show interesting exceptions - // dbg_log("Exception " + h(interrupt_nr) + " at " + h(this.previous_ip >>> 0, 8) + " (cs=" + h(this.sreg[reg_cs], 4) + ")", LOG_CPU); - // dbg_trace(LOG_CPU); - // this.debug.dump_regs(); - // this.debug.dump_state(); - //} - - this.call_interrupt_vector(interrupt_nr, false, false, 0); - throw MAGIC_CPU_EXCEPTION; -}; - -CPU.prototype.raise_exception_with_code = function(interrupt_nr, error_code) -{ - dbg_assert(typeof error_code === "number"); - - //if(DEBUG) - //{ - // dbg_log("Exception " + h(interrupt_nr) + " err=" + h(error_code) + " at " + h(this.previous_ip >>> 0, 8) + " (cs=" + h(this.sreg[reg_cs], 4) + ")", LOG_CPU); - // dbg_trace(LOG_CPU); - // this.debug.dump_regs(); - //} - - this.call_interrupt_vector(interrupt_nr, false, true, error_code); - throw MAGIC_CPU_EXCEPTION; -}; - -CPU.prototype.trigger_de = function() -{ - this.instruction_pointer[0] = this.previous_ip[0]; - this.raise_exception(0); -}; - -CPU.prototype.trigger_ud = function() -{ - this.instruction_pointer[0] = this.previous_ip[0]; - this.raise_exception(6); -}; - -CPU.prototype.trigger_nm = function() -{ - this.instruction_pointer[0] = this.previous_ip[0]; - this.raise_exception(7); -}; - -CPU.prototype.trigger_ts = function(code) -{ - this.instruction_pointer[0] = this.previous_ip[0]; - this.raise_exception_with_code(10, code); -}; - -CPU.prototype.trigger_gp = function(code) -{ - this.instruction_pointer[0] = this.previous_ip[0]; - this.raise_exception_with_code(13, code); -}; - -CPU.prototype.trigger_np = function(code) -{ - this.instruction_pointer[0] = this.previous_ip[0]; - this.raise_exception_with_code(11, code); -}; - -CPU.prototype.trigger_ss = function(code) -{ - this.instruction_pointer[0] = this.previous_ip[0]; - this.raise_exception_with_code(12, code); -}; - -// used before fpu instructions -CPU.prototype.task_switch_test = function() -{ - if(this.cr[0] & (CR0_EM | CR0_TS)) - { - this.trigger_nm(); - } -}; - -// used before mmx instructions -CPU.prototype.task_switch_test_mmx = function() -{ - if(this.cr[0] & (CR0_EM | CR0_TS)) - { - if(this.cr[0] & CR0_TS) - { - this.trigger_nm(); - } - else - { - this.trigger_ud(); - } - } -}; - CPU.prototype.todo = function() { if(DEBUG) @@ -3529,82 +2855,15 @@ CPU.prototype.unimplemented_sse = function() this.trigger_ud(); }; -CPU.prototype.get_seg_prefix_ds = function(offset) -{ - offset = offset || 0; - return this.get_seg_prefix(reg_ds) + offset | 0; -}; - -CPU.prototype.get_seg_prefix_ss = function(offset) -{ - offset = offset || 0; - return this.get_seg_prefix(reg_ss) + offset | 0; -}; - -CPU.prototype.get_seg_prefix_cs = function(offset) -{ - offset = offset || 0; - return this.get_seg_prefix(reg_cs) + offset | 0; -}; - -/** - * Get segment base by prefix or default - * @param {number} default_segment - */ -CPU.prototype.get_seg_prefix = function(default_segment /*, offset*/) -{ - var prefix = this.prefixes[0] & PREFIX_MASK_SEGMENT; - - if(prefix) - { - if(prefix === SEG_PREFIX_ZERO) - { - return 0; - } - else - { - return this.get_seg(prefix - 1 /*, offset*/); - } - } - else - { - return this.get_seg(default_segment /*, offset*/); - } -}; - -/** - * Get segment base - * @param {number} segment - */ -CPU.prototype.get_seg = function(segment /*, offset*/) -{ - dbg_assert(segment >= 0 && segment < 8); - - if(this.protected_mode[0]) - { - if(this.segment_is_null[segment]) - { - dbg_assert(segment !== reg_cs && segment !== reg_ss); - dbg_trace(); - dbg_log("#gp Use null segment: " + segment + " sel=" + h(this.sreg[segment], 4), LOG_CPU); - - this.trigger_gp(0); - } - - // TODO: - // - validate segment limits - // - validate if segment is writable - } - - return this.segment_offsets[segment]; -}; - CPU.prototype.pic_call_irq = function(int) { + //dbg_log("pic_call_irq", LOG_CPU); + try { this.previous_ip[0] = this.instruction_pointer[0]; this.call_interrupt_vector(int, false, false, 0); + //dbg_log("to " + h(this.instruction_pointer[0] >>> 0), LOG_CPU); } catch(e) { @@ -4347,325 +3606,6 @@ CPU.prototype.invlpg = function(addr) this.last_virt_esp[0] = -1; }; -CPU.prototype.translate_address_read = function(addr) -{ - if(!this.paging[0]) - { - return addr; - } - - if(this.cpl[0] === 3) - { - return this.translate_address_user_read(addr); - } - else - { - return this.translate_address_system_read(addr); - } -}; - -CPU.prototype.translate_address_write = function(addr) -{ - if(!this.paging[0]) - { - return addr; - } - - if(this.cpl[0] === 3) - { - return this.translate_address_user_write(addr); - } - else - { - return this.translate_address_system_write(addr); - } -}; - -CPU.prototype.translate_address_user_write = function(addr) -{ - if(!this.paging[0]) - { - return addr; - } - - var base = addr >>> 12; - - if(this.tlb_info[base] & TLB_USER_WRITE) - { - return this.tlb_data[base] ^ addr; - } - else - { - return this.do_page_translation(addr, 1, 1) | addr & 0xFFF; - } -}; - -CPU.prototype.translate_address_user_read = function(addr) -{ - if(!this.paging[0]) - { - return addr; - } - - var base = addr >>> 12; - - if(this.tlb_info[base] & TLB_USER_READ) - { - return this.tlb_data[base] ^ addr; - } - else - { - return this.do_page_translation(addr, 0, 1) | addr & 0xFFF; - } -}; - -CPU.prototype.translate_address_system_write = function(addr) -{ - if(!this.paging[0]) - { - return addr; - } - - var base = addr >>> 12; - - if(this.tlb_info[base] & TLB_SYSTEM_WRITE) - { - return this.tlb_data[base] ^ addr; - } - else - { - return this.do_page_translation(addr, 1, 0) | addr & 0xFFF; - } -}; - -CPU.prototype.translate_address_system_read = function(addr) -{ - if(!this.paging[0]) - { - return addr; - } - - var base = addr >>> 12; - - if(this.tlb_info[base] & TLB_SYSTEM_READ) - { - return this.tlb_data[base] ^ addr; - } - else - { - return this.do_page_translation(addr, 0, 0) | addr & 0xFFF; - } -}; - -/** - * @return {number} - */ -CPU.prototype.do_page_translation = function(addr, for_writing, user) -{ - var page = addr >>> 12, - page_dir_addr = (this.cr[3] >>> 2) + (page >> 10) | 0, - page_dir_entry = this.mem32s[page_dir_addr], - high, - can_write = true, - global, - cachable = true, - allow_user = true; - - dbg_assert(addr < 0x80000000); - - if(!(page_dir_entry & 1)) - { - // to do at this place: - // - // - set cr2 = addr (which caused the page fault) - // - call_interrupt_vector with id 14, error code 0-7 (requires information if read or write) - // - prevent execution of the function that triggered this call - //dbg_log("#PF not present", LOG_CPU); - - this.cr[2] = addr; - this.trigger_pagefault(for_writing, user, 0); - - // never reached as this.trigger_pagefault throws up - dbg_assert(false); - } - - if((page_dir_entry & 2) === 0) - { - can_write = false; - - if(for_writing && (user || (this.cr[0] & CR0_WP))) - { - this.cr[2] = addr; - this.trigger_pagefault(for_writing, user, 1); - dbg_assert(false); - } - } - - if((page_dir_entry & 4) === 0) - { - allow_user = false; - - if(user) - { - // "Page Fault: page table accessed by non-supervisor"; - //dbg_log("#PF supervisor", LOG_CPU); - this.cr[2] = addr; - this.trigger_pagefault(for_writing, user, 1); - dbg_assert(false); - } - } - - if(page_dir_entry & this.page_size_extensions[0]) - { - // size bit is set - - // set the accessed and dirty bits - this.mem32s[page_dir_addr] = page_dir_entry | 0x20 | for_writing << 6; - - high = (page_dir_entry & 0xFFC00000) | (addr & 0x3FF000); - global = page_dir_entry & 0x100; - } - else - { - var page_table_addr = ((page_dir_entry & 0xFFFFF000) >>> 2) + (page & 0x3FF) | 0, - page_table_entry = this.mem32s[page_table_addr]; - - if((page_table_entry & 1) === 0) - { - //dbg_log("#PF not present table", LOG_CPU); - this.cr[2] = addr; - this.trigger_pagefault(for_writing, user, 0); - dbg_assert(false); - } - - if((page_table_entry & 2) === 0) - { - can_write = false; - - if(for_writing && (user || (this.cr[0] & CR0_WP))) - { - //dbg_log("#PF not writable page", LOG_CPU); - this.cr[2] = addr; - this.trigger_pagefault(for_writing, user, 1); - dbg_assert(false); - } - } - - if((page_table_entry & 4) === 0) - { - allow_user = false; - - if(user) - { - //dbg_log("#PF not supervisor page", LOG_CPU); - this.cr[2] = addr; - this.trigger_pagefault(for_writing, user, 1); - dbg_assert(false); - } - } - - // set the accessed and dirty bits - this.write_aligned32(page_dir_addr, page_dir_entry | 0x20); - this.write_aligned32(page_table_addr, page_table_entry | 0x20 | for_writing << 6); - - high = page_table_entry & 0xFFFFF000; - global = page_table_entry & 0x100; - } - - this.tlb_data[page] = high ^ page << 12; - - var allowed_flag; - - if(allow_user) - { - if(can_write) - { - allowed_flag = TLB_SYSTEM_READ | TLB_SYSTEM_WRITE | TLB_USER_READ | TLB_USER_WRITE; - } - else - { - // TODO: Consider if cr0.wp is not set - allowed_flag = TLB_SYSTEM_READ | TLB_USER_READ; - } - } - else - { - if(can_write) - { - allowed_flag = TLB_SYSTEM_READ | TLB_SYSTEM_WRITE; - } - else - { - allowed_flag = TLB_SYSTEM_READ; - } - } - - this.tlb_info[page] = allowed_flag; - - if(global && (this.cr[4] & CR4_PGE)) - { - this.tlb_info_global[page] = allowed_flag; - } - - return high; -}; - -CPU.prototype.writable_or_pagefault = function(addr, size) -{ - dbg_assert(size < 0x1000, "not supported yet"); - dbg_assert(size > 0); - - if(!this.paging[0]) - { - return; - } - - var user = this.cpl[0] === 3 ? 1 : 0, - mask = user ? TLB_USER_WRITE : TLB_SYSTEM_WRITE, - page = addr >>> 12; - - if((this.tlb_info[page] & mask) === 0) - { - this.do_page_translation(addr, 1, user); - } - - if((addr & 0xFFF) + size - 1 >= 0x1000) - { - if((this.tlb_info[page + 1 | 0] & mask) === 0) - { - this.do_page_translation(addr + size - 1 | 0, 1, user); - } - } -}; - -CPU.prototype.trigger_pagefault = function(write, user, present) -{ - if(LOG_PAGE_FAULTS) - { - dbg_log("page fault w=" + write + " u=" + user + " p=" + present + - " eip=" + h(this.previous_ip[0] >>> 0, 8) + - " cr2=" + h(this.cr[2] >>> 0, 8), LOG_CPU); - dbg_trace(LOG_CPU); - } - - if(this.page_fault[0]) - { - dbg_trace(LOG_CPU); - throw this.debug.unimpl("Double fault"); - } - - // invalidate tlb entry - var page = this.cr[2] >>> 12; - this.tlb_info[page] = 0; - this.tlb_info_global[page] = 0; - - this.instruction_pointer[0] = this.previous_ip[0]; - this.page_fault[0] = 1; - this.call_interrupt_vector(14, false, true, user << 2 | write << 1 | present); - - throw MAGIC_CPU_EXCEPTION; -}; - CPU.prototype.is_osize_32 = function() { return Boolean(this.is_32[0]) !== ((this.prefixes[0] & PREFIX_MASK_OPSIZE) === PREFIX_MASK_OPSIZE); @@ -4676,52 +3616,6 @@ CPU.prototype.is_asize_32 = function() return Boolean(this.is_32[0]) !== ((this.prefixes[0] & PREFIX_MASK_ADDRSIZE) === PREFIX_MASK_ADDRSIZE); }; -CPU.prototype.get_reg_asize = function(reg) -{ - dbg_assert(reg === reg_ecx || reg === reg_esi || reg === reg_edi); - var r = this.reg32s[reg]; - - if(this.is_asize_32()) - { - return r; - } - else - { - return r & 0xFFFF; - } -}; - -CPU.prototype.set_ecx_asize = function(value) -{ - if(this.is_asize_32()) - { - this.reg32s[reg_ecx] = value; - } - else - { - this.reg16[reg_cx] = value; - } -}; - -CPU.prototype.add_reg_asize = function(reg, value) -{ - dbg_assert(reg === reg_ecx || reg === reg_esi || reg === reg_edi); - if(this.is_asize_32()) - { - this.reg32s[reg] += value; - } - else - { - this.reg16[reg << 1] += value; - } -}; - -CPU.prototype.decr_ecx_asize = function() -{ - return this.is_asize_32() ? --this.reg32s[reg_ecx] : --this.reg16[reg_cx]; -}; - -// moved from misc_instr.js (now deleted) CPU.prototype.loopne = function(imm8s) { if(this.decr_ecx_asize() && !this.getzf()) @@ -4774,20 +3668,6 @@ CPU.prototype.jcxz = function(imm8s) } }; -/** @return {number} */ -CPU.prototype.getpf = function() -{ - if(this.flags_changed[0] & flag_parity) - { - // inverted lookup table - return 0x9669 << 2 >> ((this.last_result[0] ^ this.last_result[0] >> 4) & 0xF) & flag_parity; - } - else - { - return this.flags[0] & flag_parity; - } -}; - CPU.prototype.popa16 = function() { this.translate_address_read(this.get_stack_pointer(0)); @@ -4900,6 +3780,7 @@ CPU.prototype.bswap = function(reg) this.reg32s[reg] = temp >>> 24 | temp << 24 | (temp >> 8 & 0xFF00) | (temp << 8 & 0xFF0000); }; + // Closure Compiler's way of exporting if(typeof window !== "undefined") { diff --git a/src/fpu.js b/src/fpu.js deleted file mode 100644 index 37b75a6a..00000000 --- a/src/fpu.js +++ /dev/null @@ -1,1655 +0,0 @@ -"use strict"; - -/** @const */ -var FPU_LOG_OP = false; - -var - /** @const */ - FPU_C0 = 0x100, - /** @const */ - FPU_C1 = 0x200, - /** @const */ - FPU_C2 = 0x400, - /** @const */ - FPU_C3 = 0x4000, - /** @const */ - FPU_RESULT_FLAGS = FPU_C0 | FPU_C1 | FPU_C2 | FPU_C3, - /** @const */ - FPU_STACK_TOP = 0x3800; - -var - // precision, round & infinity control - /** @const */ - FPU_PC = 3 << 8, - /** @const */ - FPU_RC = 3 << 10, - /** @const */ - FPU_IF = 1 << 12; - -// exception bits in the status word -var - /** @const */ - FPU_EX_SF = 1 << 6, - /** @const */ - FPU_EX_P = 1 << 5, - /** @const */ - FPU_EX_U = 1 << 4, - /** @const */ - FPU_EX_O = 1 << 3, - /** @const */ - FPU_EX_Z = 1 << 2, - /** @const */ - FPU_EX_D = 1 << 1, - /** @const */ - FPU_EX_I = 1 << 0; - -var - /** @const */ - TWO_POW_63 = 0x8000000000000000; - -/** - * @constructor - * @param {CPU} cpu - */ -function FPU(cpu) -{ - // TODO: - // - Precision Control - // - QNaN, unordered comparison - // - Exceptions - - this.cpu = cpu; - - // Why no Float80Array :-( - this.st = new Float64Array(cpu.wm.memory.buffer, 968, 8); - - // used for conversion - /** @const */ this.float32 = new Float32Array(cpu.wm.memory.buffer, 956, 1); - /** @const */ this.float32_byte = new Uint8Array(this.float32.buffer, 956, 4); - /** @const */ this.float32_int = new Int32Array(this.float32.buffer, 956, 1); - /** @const */ this.float64 = new Float64Array(cpu.wm.memory.buffer, 960, 1); - /** @const */ this.float64_byte = new Uint8Array(this.float64.buffer, 960, 8); - /** @const */ this.float64_int = new Int32Array(this.float64.buffer, 960, 2); - - /** @const */ this.st8 = new Uint8Array(this.st.buffer, 968, 8 << 3); - /** @const */ this.st32 = new Int32Array(this.st.buffer, 968, 8 << 1); - - - // bitmap of which stack registers are empty - this.stack_empty = new Int32Array(cpu.wm.memory.buffer, 816, 1); - this.stack_empty[0] = 0xff; - this.stack_ptr = new Uint32Array(cpu.wm.memory.buffer, 1032, 1); - this.stack_ptr[0] = 0; - - this.control_word = new Int32Array(cpu.wm.memory.buffer, 1036, 1); - this.control_word[0] = 0x37F; - this.status_word = new Int32Array(cpu.wm.memory.buffer, 1040, 1); - this.status_word[0] = 0; - this.fpu_ip = new Int32Array(cpu.wm.memory.buffer, 1048, 1); - this.fpu_ip[0] = 0; - this.fpu_ip_selector = new Int32Array(cpu.wm.memory.buffer, 1052, 1); - this.fpu_ip_selector[0] = 0; - this.fpu_opcode = new Int32Array(cpu.wm.memory.buffer, 1044, 1); - this.fpu_opcode[0] = 0; - this.fpu_dp = new Int32Array(cpu.wm.memory.buffer, 1056, 1); - this.fpu_dp[0] = 0; - this.fpu_dp_selector = new Int32Array(cpu.wm.memory.buffer, 1060, 1); - this.fpu_dp_selector[0] = 0; - - /** @const */ - this.indefinite_nan = NaN; - - /** @const */ - this.constants = new Float64Array([ - 1, Math.log(10) / Math.LN2, Math.LOG2E, Math.PI, - Math.log(2) / Math.LN10, Math.LN2, 0 - ]); - - this.wasm_patch(cpu.wm); -} - -FPU.prototype.wasm_patch = function(wm) -{ - this.set_tag_word = wm.exports["_fpu_set_tag_word"]; - this.fcomi = wm.exports["_fpu_fcomi"]; - this.load_status_word = wm.exports["_fpu_load_status_word"]; - this.store_m80 = wm.exports["_fpu_store_m80"]; - this.set_status_word = wm.exports["_fpu_set_status_word"]; - this.load_m80 = wm.exports["_fpu_load_m80"]; -}; - -FPU.prototype.get_state = function() -{ - var state = []; - - state[0] = this.st; - state[1] = this.stack_empty[0]; - state[2] = this.stack_ptr[0]; - state[3] = this.control_word[0]; - state[4] = this.fpu_dp_selector[0]; - state[5] = this.fpu_ip[0]; - state[6] = this.fpu_ip_selector[0]; - state[7] = this.fpu_dp[0]; - state[8] = this.fpu_dp_selector[0]; - state[9] = this.fpu_opcode[0]; - - return state; -}; - -FPU.prototype.set_state = function(state) -{ - this.st.set(state[0]); - this.stack_empty[0] = state[1]; - this.stack_ptr[0] = state[2]; - this.control_word[0] = state[3]; - this.fpu_dp_selector[0] = state[4]; - this.fpu_ip[0] = state[5]; - this.fpu_ip_selector[0] = state[6]; - this.fpu_dp[0] = state[7]; - this.fpu_dp_selector[0] = state[8]; - this.fpu_opcode[0] = state[9]; -}; - -FPU.prototype.fpu_unimpl = function() -{ - dbg_trace(); - if(DEBUG) throw "fpu: unimplemented"; - else this.cpu.trigger_ud(); -}; - -FPU.prototype.stack_fault = function() -{ - // TODO: Interrupt - this.status_word[0] |= FPU_EX_SF | FPU_EX_I; -}; - -FPU.prototype.invalid_arithmatic = function() -{ - this.status_word[0] |= FPU_EX_I; -}; - -FPU.prototype.fcom = function(y) -{ - var x = this.get_st0(); - - this.status_word[0] &= ~FPU_RESULT_FLAGS; - - if(x > y) - { - } - else if(y > x) - { - this.status_word[0] |= FPU_C0; - } - else if(x === y) - { - this.status_word[0] |= FPU_C3; - } - else - { - this.status_word[0] |= FPU_C0 | FPU_C2 | FPU_C3; - } -}; - -FPU.prototype.fucom = function(y) -{ - // TODO - this.fcom(y); -}; - - -FPU.prototype.fcomi = function(y) -{ - var x = this.st[this.stack_ptr[0]]; - - this.cpu.flags_changed[0] &= ~(1 | flag_parity | flag_zero); - this.cpu.flags[0] &= ~(1 | flag_parity | flag_zero); - - if(x > y) - { - } - else if(y > x) - { - this.cpu.flags[0] |= 1; - } - else if(x === y) - { - this.cpu.flags[0] |= flag_zero; - } - else - { - this.cpu.flags[0] |= 1 | flag_parity | flag_zero; - } -}; - -FPU.prototype.fucomi = function(y) -{ - // TODO - this.fcomi(y); -}; - -FPU.prototype.ftst = function(x) -{ - this.status_word[0] &= ~FPU_RESULT_FLAGS; - - if(isNaN(x)) - { - this.status_word[0] |= FPU_C3 | FPU_C2 | FPU_C0; - } - else if(x === 0) - { - this.status_word[0] |= FPU_C3; - } - else if(x < 0) - { - this.status_word[0] |= FPU_C0; - } - - // TODO: unordered (x is nan, etc) -}; - -FPU.prototype.fxam = function(x) -{ - this.status_word[0] &= ~FPU_RESULT_FLAGS; - this.status_word[0] |= this.sign(0) << 9; - - if(this.stack_empty[0] >> this.stack_ptr[0] & 1) - { - this.status_word[0] |= FPU_C3 | FPU_C0; - } - else if(isNaN(x)) - { - this.status_word[0] |= FPU_C0; - } - else if(x === 0) - { - this.status_word[0] |= FPU_C3; - } - else if(x === Infinity || x === -Infinity) - { - this.status_word[0] |= FPU_C2 | FPU_C0; - } - else - { - this.status_word[0] |= FPU_C2; - } - // TODO: - // Unsupported, Denormal -}; - -FPU.prototype.finit = function() -{ - this.control_word[0] = 0x37F; - this.status_word[0] = 0; - this.fpu_ip[0] = 0; - this.fpu_dp[0] = 0; - this.fpu_opcode[0] = 0; - - this.stack_empty[0] = 0xFF; - this.stack_ptr[0] = 0; -}; - -FPU.prototype.load_status_word = function() -{ - return this.status_word[0] & ~(7 << 11) | this.stack_ptr[0] << 11; -}; - -FPU.prototype.set_status_word = function(sw) -{ - this.status_word[0] = sw & ~(7 << 11); - this.stack_ptr[0] = sw >> 11 & 7; -}; - -FPU.prototype.load_tag_word = function() -{ - var tag_word = 0, - value; - - for(var i = 0; i < 8; i++) - { - value = this.st[i]; - - if(this.stack_empty[0] >> i & 1) - { - tag_word |= 3 << (i << 1); - } - else if(value === 0) - { - tag_word |= 1 << (i << 1); - } - else if(!isFinite(value)) - { - tag_word |= 2 << (i << 1); - } - } - - //dbg_log("load tw=" + h(tag_word) + " se=" + h(this.stack_empty[0]) + " sp=" + this.stack_ptr[0], LOG_FPU); - - return tag_word; -}; - -FPU.prototype.set_tag_word = function(tag_word) -{ - this.stack_empty[0] = 0; - - for(var i = 0; i < 8; i++) - { - this.stack_empty[0] |= (tag_word >> i) & (tag_word >> i + 1) & 1 << i; - } - - //dbg_log("set_tag_word tw=" + h(tag_word) + " se=" + h(this.stack_empty), LOG_FPU); -}; - -FPU.prototype.fstenv = function(addr) -{ - if(this.cpu.is_osize_32()) - { - this.cpu.writable_or_pagefault(addr, 26); - - this.cpu.safe_write16(addr, this.control_word[0]); - - this.cpu.safe_write16(addr + 4, this.load_status_word()); - this.cpu.safe_write16(addr + 8, this.load_tag_word()); - - this.cpu.safe_write32(addr + 12, this.fpu_ip[0]); - this.cpu.safe_write16(addr + 16, this.fpu_ip_selector[0]); - this.cpu.safe_write16(addr + 18, this.fpu_opcode[0]); - this.cpu.safe_write32(addr + 20, this.fpu_dp[0]); - this.cpu.safe_write16(addr + 24, this.fpu_dp_selector[0]); - } - else - { - this.fpu_unimpl(); - } -}; - -FPU.prototype.fldenv = function(addr) -{ - if(this.cpu.is_osize_32()) - { - this.control_word[0] = this.cpu.safe_read16(addr); - - this.set_status_word(this.cpu.safe_read16(addr + 4)); - this.set_tag_word(this.cpu.safe_read16(addr + 8)); - - this.fpu_ip[0] = this.cpu.safe_read32s(addr + 12); - this.fpu_ip_selector[0] = this.cpu.safe_read16(addr + 16); - this.fpu_opcode[0] = this.cpu.safe_read16(addr + 18); - this.fpu_dp[0] = this.cpu.safe_read32s(addr + 20); - this.fpu_dp_selector[0] = this.cpu.safe_read16(addr + 24); - } - else - { - this.fpu_unimpl(); - } -}; - -FPU.prototype.fsave = function(addr) -{ - this.cpu.writable_or_pagefault(addr, 108); - - this.fstenv(addr); - addr += 28; - - for(var i = 0; i < 8; i++) - { - this.store_m80(addr, this.st[this.stack_ptr[0] + i & 7]); - addr += 10; - } - - //dbg_log("save st=" + this.stack_ptr[0] + " " + [].slice.call(this.st), LOG_FPU); - - this.finit(); -}; - -FPU.prototype.frstor = function(addr) -{ - this.fldenv(addr); - addr += 28; - - for(var i = 0; i < 8; i++) - { - this.st[(i + this.stack_ptr[0]) & 7] = this.load_m80(addr); - addr += 10; - } - - //dbg_log("rstor st=" + this.stack_ptr[0] + " " + [].slice.call(this.st), LOG_FPU); -}; - -FPU.prototype.fxtract = function() -{ - this.float64[0] = this.get_st0(); - - var exponent = ((this.float64_byte[7] & 0x7F) << 4 | this.float64_byte[6] >> 4) - 0x3FF; - - this.float64_byte[7] = 0x3F | (this.float64_byte[7] & 0x80); - this.float64_byte[6] |= 0xF0; - - this.st[this.stack_ptr[0]] = exponent; - this.push(this.float64[0]); -}; - -FPU.prototype.integer_round = function(f) -{ - var rc = this.control_word[0] >> 10 & 3; - - if(rc === 0) - { - // Round to nearest, or even if equidistant - var rounded = Math.round(f); - - if(rounded - f === 0.5 && (rounded % 2)) - { - // Special case: Math.round rounds to positive infinity - // if equidistant - rounded--; - } - - return rounded; - } - // rc=3 is truncate -> floor for positive numbers - else if(rc === 1 || (rc === 3 && f > 0)) - { - return Math.floor(f); - } - else - { - return Math.ceil(f); - } -}; - -FPU.prototype.truncate = function(x) -{ - return x > 0 ? Math.floor(x) : Math.ceil(x); -}; - -FPU.prototype.push = function(x) -{ - this.stack_ptr[0] = this.stack_ptr[0] - 1 & 7; - - if(this.stack_empty[0] >> this.stack_ptr[0] & 1) - { - this.status_word[0] &= ~FPU_C1; - this.stack_empty[0] &= ~(1 << this.stack_ptr[0]); - this.st[this.stack_ptr[0]] = x; - } - else - { - this.status_word[0] |= FPU_C1; - this.stack_fault(); - this.st[this.stack_ptr[0]] = this.indefinite_nan; - } -}; - -FPU.prototype.pop = function() -{ - this.stack_empty[0] |= 1 << this.stack_ptr[0]; - this.stack_ptr[0] = this.stack_ptr[0] + 1 & 7; -}; - -FPU.prototype.get_sti = function(i) -{ - dbg_assert(typeof i === "number" && i >= 0 && i < 8); - - i = i + this.stack_ptr[0] & 7; - - if(this.stack_empty[0] >> i & 1) - { - this.status_word[0] &= ~FPU_C1; - this.stack_fault(); - return this.indefinite_nan; - } - else - { - return this.st[i]; - } -}; - -FPU.prototype.get_st0 = function() -{ - if(this.stack_empty[0] >> this.stack_ptr[0] & 1) - { - this.status_word[0] &= ~FPU_C1; - this.stack_fault(); - return this.indefinite_nan; - } - else - { - return this.st[this.stack_ptr[0]]; - } -}; - -FPU.prototype.load_m80 = function(addr) -{ - var exponent = this.cpu.safe_read16(addr + 8), - sign, - low = this.cpu.safe_read32s(addr) >>> 0, - high = this.cpu.safe_read32s(addr + 4) >>> 0; - - sign = exponent >> 15; - exponent &= ~0x8000; - - if(exponent === 0) - { - // TODO: denormal numbers - return 0; - } - - if(exponent < 0x7FFF) - { - exponent -= 0x3FFF; - } - else - { - // TODO: NaN, Infinity - //dbg_log("Load m80 TODO", LOG_FPU); - this.float64_byte[7] = 0x7F | sign << 7; - this.float64_byte[6] = 0xF0 | high >> 30 << 3 & 0x08; - - this.float64_byte[5] = 0; - this.float64_byte[4] = 0; - - this.float64_int[0] = 0; - - return this.float64[0]; - } - - // Note: some bits might be lost at this point - var mantissa = low + 0x100000000 * high; - - if(sign) - { - mantissa = -mantissa; - } - - //console.log("m: " + mantissa); - //console.log("e: " + exponent); - //console.log("s: " + this.sign); - //console.log("f: " + mantissa * Math.pow(2, exponent - 63)); - - // Simply compute the 64 bit floating point number. - // An alternative write the mantissa, sign and exponent in the - // float64_byte and return float64[0] - - return mantissa * Math.pow(2, exponent - 63); -}; - -FPU.prototype.store_m80 = function(addr, n) -{ - this.float64[0] = n; - - var sign = this.float64_byte[7] & 0x80, - exponent = (this.float64_byte[7] & 0x7f) << 4 | this.float64_byte[6] >> 4, - low, - high; - - if(exponent === 0x7FF) - { - // all bits set (NaN and infinity) - exponent = 0x7FFF; - low = 0; - high = 0x80000000 | (this.float64_int[1] & 0x80000) << 11; - } - else if(exponent === 0) - { - // zero and denormal numbers - // Just assume zero for now - low = 0; - high = 0; - } - else - { - exponent += 0x3FFF - 0x3FF; - - // does the mantissa need to be adjusted? - low = this.float64_int[0] << 11; - high = 0x80000000 | (this.float64_int[1] & 0xFFFFF) << 11 | (this.float64_int[0] >>> 21); - } - - dbg_assert(exponent >= 0 && exponent < 0x8000); - - this.cpu.safe_write32(addr, low); - this.cpu.safe_write32(addr + 4, high); - - this.cpu.safe_write16(addr + 8, sign << 8 | exponent); -}; - -FPU.prototype.load_m64 = function(addr) -{ - var low = this.cpu.safe_read32s(addr), - high = this.cpu.safe_read32s(addr + 4); - - this.float64_int[0] = low; - this.float64_int[1] = high; - - return this.float64[0]; -}; - -FPU.prototype.store_m64 = function(addr, i) -{ - this.cpu.writable_or_pagefault(addr, 8); - - this.float64[0] = this.get_sti(i); - - this.cpu.safe_write32(addr, this.float64_int[0]); - this.cpu.safe_write32(addr + 4, this.float64_int[1]); -}; - -FPU.prototype.load_m32 = function(addr) -{ - this.float32_int[0] = this.cpu.safe_read32s(addr); - - return this.float32[0]; -}; - -FPU.prototype.store_m32 = function(addr, x) -{ - this.float32[0] = x; - - this.cpu.safe_write32(addr, this.float32_int[0]); -}; - -// sign of a number on the stack -FPU.prototype.sign = function(i) -{ - return this.st8[(this.stack_ptr[0] + i & 7) << 3 | 7] >> 7; -}; - - -FPU.prototype.dbg_log_fpu_op = function(op, imm8) -{ - if(!FPU_LOG_OP) - { - return; - } - - if(imm8 >= 0xC0) - { - dbg_log(h(op, 2) + " " + h(imm8, 2) + "/" + (imm8 >> 3 & 7) + "/" + (imm8 & 7) + - " @" + h(this.cpu.instruction_pointer[0] >>> 0, 8) + " sp=" + this.stack_ptr[0] + " st=" + h(this.stack_empty[0], 2), LOG_FPU); - } - else - { - dbg_log(h(op, 2) + " /" + imm8 + - " @" + h(this.cpu.instruction_pointer[0] >>> 0, 8) + " sp=" + this.stack_ptr[0] + " st=" + h(this.stack_empty[0], 2), LOG_FPU); - } -}; - - -FPU.prototype.fwait = function() -{ - // NOP unless FPU instructions run in parallel with CPU instructions -}; - - -FPU.prototype.op_D8_reg = function(imm8) -{ - this.dbg_log_fpu_op(0xD8, imm8); - - var mod = imm8 >> 3 & 7, - low = imm8 & 7, - sti = this.get_sti(low), - st0 = this.get_st0(); - - switch(mod) - { - case 0: - // fadd - this.st[this.stack_ptr[0]] = st0 + sti; - break; - case 1: - // fmul - this.st[this.stack_ptr[0]] = st0 * sti; - break; - case 2: - // fcom - this.fcom(sti); - break; - case 3: - // fcomp - this.fcom(sti); - this.pop(); - break; - case 4: - // fsub - this.st[this.stack_ptr[0]] = st0 - sti; - break; - case 5: - // fsubr - this.st[this.stack_ptr[0]] = sti - st0; - break; - case 6: - // fdiv - this.st[this.stack_ptr[0]] = st0 / sti; - break; - case 7: - // fdivr - this.st[this.stack_ptr[0]] = sti / st0; - break; - default: - dbg_assert(false); - } -}; - -FPU.prototype.op_D8_mem = function(mod, addr) -{ - this.dbg_log_fpu_op(0xD8, mod); - - var m32 = this.load_m32(addr); - var st0 = this.get_st0(); - - switch(mod) - { - case 0: - // fadd - this.st[this.stack_ptr[0]] = st0 + m32; - break; - case 1: - // fmul - this.st[this.stack_ptr[0]] = st0 * m32; - break; - case 2: - // fcom - this.fcom(m32); - break; - case 3: - // fcomp - this.fcom(m32); - this.pop(); - break; - case 4: - // fsub - this.st[this.stack_ptr[0]] = st0 - m32; - break; - case 5: - // fsubr - this.st[this.stack_ptr[0]] = m32 - st0; - break; - case 6: - // fdiv - this.st[this.stack_ptr[0]] = st0 / m32; - break; - case 7: - // fdivr - this.st[this.stack_ptr[0]] = m32 / st0; - break; - default: - dbg_assert(false); - } -}; - -FPU.prototype.op_D9_reg = function(imm8) -{ - this.dbg_log_fpu_op(0xD9, imm8); - - var mod = imm8 >> 3 & 7, - low = imm8 & 7; - - switch(mod) - { - case 0: - // fld - var sti = this.get_sti(low); - this.push(sti); - break; - case 1: - // fxch - var sti = this.get_sti(low); - - this.st[this.stack_ptr[0] + low & 7] = this.get_st0(); - this.st[this.stack_ptr[0]] = sti; - break; - case 2: - switch(low) - { - case 0: - // fnop - break; - default: - dbg_log(low); - this.fpu_unimpl(); - } - break; - case 3: - // fstp1 - this.fpu_unimpl(); - break; - case 4: - var st0 = this.get_st0(); - - switch(low) - { - case 0: - // fchs - this.st[this.stack_ptr[0]] = -st0; - break; - case 1: - // fabs - this.st[this.stack_ptr[0]] = Math.abs(st0); - break; - case 4: - this.ftst(st0); - break; - case 5: - this.fxam(st0); - break; - default: - dbg_log(low); - this.fpu_unimpl(); - } - break; - case 5: - this.push(this.constants[low]); - break; - case 6: - var st0 = this.get_st0(); - - switch(low) - { - case 0: - // f2xm1 - this.st[this.stack_ptr[0]] = Math.pow(2, st0) - 1; - break; - case 1: - // fyl2x - this.st[this.stack_ptr[0] + 1 & 7] = this.get_sti(1) * Math.log(st0) / Math.LN2; - this.pop(); - break; - case 2: - // fptan - this.st[this.stack_ptr[0]] = Math.tan(st0); - this.push(1); // no bug: push constant 1 - break; - case 3: - // fpatan - this.st[this.stack_ptr[0] + 1 & 7] = Math.atan2(this.get_sti(1), st0); - this.pop(); - break; - case 4: - this.fxtract(); - break; - case 5: - // fprem1 - this.st[this.stack_ptr[0]] = st0 % this.get_sti(1); - break; - case 6: - // fdecstp - this.stack_ptr[0] = this.stack_ptr[0] - 1 & 7; - this.status_word[0] &= ~FPU_C1; - break; - case 7: - // fincstp - this.stack_ptr[0] = this.stack_ptr[0] + 1 & 7; - this.status_word[0] &= ~FPU_C1; - break; - default: - dbg_assert(false); - } - break; - case 7: - var st0 = this.get_st0(); - - switch(low) - { - case 0: - // fprem - var st1 = this.get_sti(1); - var fprem_quotient = Math.trunc(st0 / st1); - this.st[this.stack_ptr[0]] = st0 % st1; - - this.status_word[0] &= ~(FPU_C0 | FPU_C1 | FPU_C3); - if (fprem_quotient & 1) { - this.status_word[0] |= FPU_C1; - } - if (fprem_quotient & (1 << 1)) { - this.status_word[0] |= FPU_C3; - } - if (fprem_quotient & (1 << 2)) { - this.status_word[0] |= FPU_C0; - } - - this.status_word[0] &= ~FPU_C2; - break; - case 1: - // fyl2xp1: y * log2(x+1) and pop - this.st[this.stack_ptr[0] + 1 & 7] = this.get_sti(1) * Math.log(st0 + 1) / Math.LN2; - this.pop(); - break; - case 2: - this.st[this.stack_ptr[0]] = Math.sqrt(st0); - break; - case 3: - this.st[this.stack_ptr[0]] = Math.sin(st0); - this.push(Math.cos(st0)); - break; - case 4: - // frndint - this.st[this.stack_ptr[0]] = this.integer_round(st0); - break; - case 5: - // fscale - this.st[this.stack_ptr[0]] = st0 * Math.pow(2, this.truncate(this.get_sti(1))); - break; - case 6: - this.st[this.stack_ptr[0]] = Math.sin(st0); - break; - case 7: - this.st[this.stack_ptr[0]] = Math.cos(st0); - break; - default: - dbg_assert(false); - } - break; - default: - dbg_assert(false); - } -}; - -FPU.prototype.op_D9_mem = function(mod, addr) -{ - this.dbg_log_fpu_op(0xD9, mod); - - switch(mod) - { - case 0: - // fld - var data = this.load_m32(addr); - this.push(data); - break; - case 1: - // not defined - this.fpu_unimpl(); - break; - case 2: - // fst - this.store_m32(addr, this.get_st0()); - break; - case 3: - // fstp - this.store_m32(addr, this.get_st0()); - this.pop(); - break; - case 4: - this.fldenv(addr); - break; - case 5: - // fldcw - var word = this.cpu.safe_read16(addr); - this.control_word[0] = word; - break; - case 6: - this.fstenv(addr); - break; - case 7: - // fstcw - this.cpu.safe_write16(addr, this.control_word[0]); - break; - default: - dbg_assert(false); - } -}; - -FPU.prototype.op_DA_reg = function(imm8) -{ - this.dbg_log_fpu_op(0xDA, imm8); - - var mod = imm8 >> 3 & 7, - low = imm8 & 7; - - switch(mod) - { - case 0: - // fcmovb - if(this.cpu.test_b()) - { - this.st[this.stack_ptr[0]] = this.get_sti(low); - this.stack_empty[0] &= ~(1 << this.stack_ptr[0]); - } - break; - case 1: - // fcmove - if(this.cpu.test_z()) - { - this.st[this.stack_ptr[0]] = this.get_sti(low); - this.stack_empty[0] &= ~(1 << this.stack_ptr[0]); - } - break; - case 2: - // fcmovbe - if(this.cpu.test_be()) - { - this.st[this.stack_ptr[0]] = this.get_sti(low); - this.stack_empty[0] &= ~(1 << this.stack_ptr[0]); - } - break; - case 3: - // fcmovu - if(this.cpu.test_p()) - { - this.st[this.stack_ptr[0]] = this.get_sti(low); - this.stack_empty[0] &= ~(1 << this.stack_ptr[0]); - } - break; - case 5: - if(low === 1) - { - // fucompp - this.fucom(this.get_sti(1)); - this.pop(); - this.pop(); - } - else - { - dbg_log(mod); this.fpu_unimpl(); - } - break; - default: - dbg_log(mod); - this.fpu_unimpl(); - } -}; - -FPU.prototype.op_DA_mem = function(mod, addr) -{ - this.dbg_log_fpu_op(0xDA, mod); - - var m32 = this.cpu.safe_read32s(addr); - var st0 = this.get_st0(); - - switch(mod) - { - case 0: - // fadd - this.st[this.stack_ptr[0]] = st0 + m32; - break; - case 1: - // fmul - this.st[this.stack_ptr[0]] = st0 * m32; - break; - case 2: - // fcom - this.fcom(m32); - break; - case 3: - // fcomp - this.fcom(m32); - this.pop(); - break; - case 4: - // fsub - this.st[this.stack_ptr[0]] = st0 - m32; - break; - case 5: - // fsubr - this.st[this.stack_ptr[0]] = m32 - st0; - break; - case 6: - // fdiv - this.st[this.stack_ptr[0]] = st0 / m32; - break; - case 7: - // fdivr - this.st[this.stack_ptr[0]] = m32 / st0; - break; - default: - dbg_assert(false); - } -}; - -FPU.prototype.op_DB_reg = function(imm8) -{ - this.dbg_log_fpu_op(0xDB, imm8); - - var mod = imm8 >> 3 & 7, - low = imm8 & 7; - - switch(mod) - { - case 0: - // fcmovnb - if(!this.cpu.test_b()) - { - this.st[this.stack_ptr[0]] = this.get_sti(low); - this.stack_empty[0] &= ~(1 << this.stack_ptr[0]); - } - break; - case 1: - // fcmovne - if(!this.cpu.test_z()) - { - this.st[this.stack_ptr[0]] = this.get_sti(low); - this.stack_empty[0] &= ~(1 << this.stack_ptr[0]); - } - break; - case 2: - // fcmovnbe - if(!this.cpu.test_be()) - { - this.st[this.stack_ptr[0]] = this.get_sti(low); - this.stack_empty[0] &= ~(1 << this.stack_ptr[0]); - } - break; - case 3: - // fcmovnu - if(!this.cpu.test_p()) - { - this.st[this.stack_ptr[0]] = this.get_sti(low); - this.stack_empty[0] &= ~(1 << this.stack_ptr[0]); - } - break; - case 4: - if(imm8 === 0xE3) - { - this.finit(); - } - else if(imm8 === 0xE4) - { - // fsetpm - // treat as nop - } - else if(imm8 === 0xE1) - { - // fdisi - // also treat as nop - } - else if(imm8 === 0xE2) - { - // fclex - this.status_word[0] = 0; - } - else - { - dbg_log(h(imm8)); - this.fpu_unimpl(); - } - break; - case 5: - this.fucomi(this.get_sti(low)); - break; - case 6: - this.fcomi(this.get_sti(low)); - break; - default: - dbg_log(mod); - this.fpu_unimpl(); - } -}; - -FPU.prototype.op_DB_mem = function(mod, addr) -{ - this.dbg_log_fpu_op(0xDB, mod); - - switch(mod) - { - case 0: - // fild - var int32 = this.cpu.safe_read32s(addr); - this.push(int32); - break; - case 2: - // fist - var st0 = this.integer_round(this.get_st0()); - if(st0 <= 0x7FFFFFFF && st0 >= -0x80000000) - { - // TODO: Invalid operation - this.cpu.safe_write32(addr, st0); - } - else - { - this.invalid_arithmatic(); - this.cpu.safe_write32(addr, 0x80000000|0); - } - break; - case 3: - // fistp - var st0 = this.integer_round(this.get_st0()); - if(st0 <= 0x7FFFFFFF && st0 >= -0x80000000) - { - this.cpu.safe_write32(addr, st0); - } - else - { - this.invalid_arithmatic(); - this.cpu.safe_write32(addr, 0x80000000|0); - } - this.pop(); - break; - case 5: - // fld - this.push(this.load_m80(addr)); - break; - case 7: - // fstp - this.cpu.writable_or_pagefault(addr, 10); - this.store_m80(addr, this.get_st0()); - this.pop(); - break; - default: - dbg_log(mod); - this.fpu_unimpl(); - } -}; - -FPU.prototype.op_DC_reg = function(imm8) -{ - this.dbg_log_fpu_op(0xDC, imm8); - - var mod = imm8 >> 3 & 7, - low = imm8 & 7, - low_ptr = this.stack_ptr[0] + low & 7, - sti = this.get_sti(low), - st0 = this.get_st0(); - - switch(mod) - { - case 0: - // fadd - this.st[low_ptr] = sti + st0; - break; - case 1: - // fmul - this.st[low_ptr] = sti * st0; - break; - case 2: - // fcom - this.fcom(sti); - break; - case 3: - // fcomp - this.fcom(sti); - this.pop(); - break; - case 4: - // fsubr - this.st[low_ptr] = st0 - sti; - break; - case 5: - // fsub - this.st[low_ptr] = sti - st0; - break; - case 6: - // fdivr - this.st[low_ptr] = st0 / sti; - break; - case 7: - // fdiv - this.st[low_ptr] = sti / st0; - break; - default: - dbg_assert(false); - } -}; - -FPU.prototype.op_DC_mem = function(mod, addr) -{ - this.dbg_log_fpu_op(0xDC, mod); - - var m64 = this.load_m64(addr); - - var st0 = this.get_st0(); - - switch(mod) - { - case 0: - // fadd - this.st[this.stack_ptr[0]] = st0 + m64; - break; - case 1: - // fmul - this.st[this.stack_ptr[0]] = st0 * m64; - break; - case 2: - // fcom - this.fcom(m64); - break; - case 3: - // fcomp - this.fcom(m64); - this.pop(); - break; - case 4: - // fsub - this.st[this.stack_ptr[0]] = st0 - m64; - break; - case 5: - // fsubr - this.st[this.stack_ptr[0]] = m64 - st0; - break; - case 6: - // fdiv - this.st[this.stack_ptr[0]] = st0 / m64; - break; - case 7: - // fdivr - this.st[this.stack_ptr[0]] = m64 / st0; - break; - default: - dbg_assert(false); - } -}; - -FPU.prototype.op_DD_reg = function(imm8) -{ - this.dbg_log_fpu_op(0xDD, imm8); - - var mod = imm8 >> 3 & 7, - low = imm8 & 7; - - switch(mod) - { - case 0: - // ffree - this.stack_empty[0] |= 1 << (this.stack_ptr[0] + low & 7); - break; - case 2: - // fst - this.st[this.stack_ptr[0] + low & 7] = this.get_st0(); - break; - case 3: - // fstp - if(low === 0) - { - this.pop(); - } - else - { - this.st[this.stack_ptr[0] + low & 7] = this.get_st0(); - this.pop(); - } - break; - case 4: - this.fucom(this.get_sti(low)); - break; - case 5: - // fucomp - this.fucom(this.get_sti(low)); - this.pop(); - break; - default: - dbg_log(mod); - this.fpu_unimpl(); - } -}; - -FPU.prototype.op_DD_mem = function(mod, addr) -{ - this.dbg_log_fpu_op(0xDD, mod); - - switch(mod) - { - case 0: - // fld - var data = this.load_m64(addr); - this.push(data); - break; - case 1: - // fisttp - this.fpu_unimpl(); - break; - case 2: - // fst - this.store_m64(addr, 0); - break; - case 3: - // fstp - this.store_m64(addr, 0); - this.pop(); - break; - case 4: - this.frstor(addr); - break; - case 5: - // nothing - this.fpu_unimpl(); - break; - case 6: - // fsave - this.fsave(addr); - break; - case 7: - // fnstsw / store status word - this.cpu.safe_write16(addr, this.load_status_word()); - break; - default: - dbg_assert(false); - } -}; - - -FPU.prototype.op_DE_reg = function(imm8) -{ - this.dbg_log_fpu_op(0xDE, imm8); - - var mod = imm8 >> 3 & 7, - low = imm8 & 7, - low_ptr = this.stack_ptr[0] + low & 7, - sti = this.get_sti(low), - st0 = this.get_st0(); - - switch(mod) - { - case 0: - // faddp - this.st[low_ptr] = sti + st0; - break; - case 1: - // fmulp - this.st[low_ptr] = sti * st0; - break; - case 2: - // fcomp - this.fcom(sti); - break; - case 3: - // fcompp - if(low === 1) - { - this.fcom(this.st[low_ptr]); - this.pop(); - } - else - { - // not a valid encoding - dbg_log(mod); - this.fpu_unimpl(); - } - break; - case 4: - // fsubrp - this.st[low_ptr] = st0 - sti; - break; - case 5: - // fsubp - this.st[low_ptr] = sti - st0; - break; - case 6: - // fdivrp - this.st[low_ptr] = st0 / sti; - break; - case 7: - // fdivp - this.st[low_ptr] = sti / st0; - break; - default: - dbg_assert(false); - } - - this.pop(); -}; - -FPU.prototype.op_DE_mem = function(mod, addr) -{ - this.dbg_log_fpu_op(0xDE, mod); - - var m16 = this.cpu.safe_read16(addr) << 16 >> 16; - var st0 = this.get_st0(); - - switch(mod) - { - case 0: - // fadd - this.st[this.stack_ptr[0]] = st0 + m16; - break; - case 1: - // fmul - this.st[this.stack_ptr[0]] = st0 * m16; - break; - case 2: - // fcom - this.fcom(m16); - break; - case 3: - // fcomp - this.fcom(m16); - this.pop(); - break; - case 4: - // fsub - this.st[this.stack_ptr[0]] = st0 - m16; - break; - case 5: - // fsubr - this.st[this.stack_ptr[0]] = m16 - st0; - break; - case 6: - // fdiv - this.st[this.stack_ptr[0]] = st0 / m16; - break; - case 7: - // fdivr - this.st[this.stack_ptr[0]] = m16 / st0; - break; - default: - dbg_assert(false); - } -}; - -FPU.prototype.op_DF_reg = function(imm8) -{ - this.dbg_log_fpu_op(0xDF, imm8); - - var mod = imm8 >> 3 & 7, - low = imm8 & 7; - - switch(mod) - { - case 4: - if(imm8 === 0xE0) - { - // fnstsw - this.cpu.reg16[reg_ax] = this.load_status_word(); - } - else - { - dbg_log(imm8); - this.fpu_unimpl(); - } - break; - case 5: - // fucomip - this.fucomi(this.get_sti(low)); - this.pop(); - break; - case 6: - // fcomip - this.fcomi(this.get_sti(low)); - this.pop(); - break; - default: - dbg_log(mod); - this.fpu_unimpl(); - } -}; - -FPU.prototype.op_DF_mem = function(mod, addr) -{ - this.dbg_log_fpu_op(0xDF, mod); - - switch(mod) - { - case 0: - var m16 = this.cpu.safe_read16(addr) << 16 >> 16; - - this.push(m16); - break; - case 1: - // fisttp - this.fpu_unimpl(); - break; - case 2: - // fist - var st0 = this.integer_round(this.get_st0()); - if(st0 <= 0x7FFF && st0 >= -0x8000) - { - this.cpu.safe_write16(addr, st0); - } - else - { - this.invalid_arithmatic(); - this.cpu.safe_write16(addr, 0x8000); - } - break; - case 3: - // fistp - var st0 = this.integer_round(this.get_st0()); - if(st0 <= 0x7FFF && st0 >= -0x8000) - { - this.cpu.safe_write16(addr, st0); - } - else - { - this.invalid_arithmatic(); - this.cpu.safe_write16(addr, 0x8000); - } - this.pop(); - break; - case 4: - // fbld - this.fpu_unimpl(); - break; - case 5: - // fild - var low = this.cpu.safe_read32s(addr) >>> 0, - high = this.cpu.safe_read32s(addr + 4); - - var m64 = low + 0x100000000 * high; - - this.push(m64); - break; - case 6: - // fbstp - this.fpu_unimpl(); - break; - case 7: - this.cpu.writable_or_pagefault(addr, 8); - - // fistp - var st0 = this.integer_round(this.get_st0()), - st0_low, - st0_high; - - if(st0 < TWO_POW_63 && st0 >= -TWO_POW_63) - { - st0_low = st0 | 0; - st0_high = st0 / 0x100000000 | 0; - - if(st0_high === 0 && st0 < 0) - st0_high = -1; - } - else - { - // write 0x8000000000000000 - st0_low = 0; - st0_high = 0x80000000 | 0; - this.invalid_arithmatic(); - } - - this.cpu.safe_write32(addr, st0_low); - this.cpu.safe_write32(addr + 4, st0_high); - - this.pop(); - break; - default: - dbg_assert(false); - } -}; diff --git a/src/native/cpu.c b/src/native/cpu.c index 1b0bc7a9..a83e8713 100644 --- a/src/native/cpu.c +++ b/src/native/cpu.c @@ -17,6 +17,8 @@ #include "js_imports.h" #include "cpu.h" +extern void call_indirect(int32_t index); + struct code_cache jit_cache_arr[WASM_TABLE_SIZE] = {{0, 0, {0}, 0, 0, 0}}; uint32_t jit_jump = 0; @@ -49,6 +51,58 @@ int32_t get_eflags() !!getzf() << 6 | !!getsf() << 7 | !!getof() << 11; } +int32_t getiopl(void) +{ + return *flags >> 12 & 3; +} + +bool vm86_mode(void) +{ + return (*flags & FLAG_VM) == FLAG_VM; +} + +/* + * Update the flags register depending on iopl and cpl + */ +void update_eflags(int32_t new_flags) +{ + int32_t dont_update = FLAG_RF | FLAG_VM | FLAG_VIP | FLAG_VIF; + int32_t clear = ~FLAG_VIP & ~FLAG_VIF & FLAGS_MASK; + + if(*flags & FLAG_VM) + { + // other case needs to be handled in popf or iret + dbg_assert(getiopl() == 3); + + dont_update |= FLAG_IOPL; + + // don't clear vip or vif + clear |= FLAG_VIP | FLAG_VIF; + } + else + { + if(!*protected_mode) dbg_assert(*cpl == 0); + + if(*cpl) + { + // cpl > 0 + // cannot update iopl + dont_update |= FLAG_IOPL; + + if(*cpl > getiopl()) + { + // cpl > iopl + // cannot update interrupt flag + dont_update |= FLAG_INTERRUPT; + } + } + } + + *flags = (new_flags ^ ((*flags ^ new_flags) & dont_update)) & clear | FLAGS_DEFAULT; + + *flags_changed = 0; +} + void trigger_pagefault(bool write, bool user, bool present) { if(LOG_PAGE_FAULTS) @@ -266,7 +320,7 @@ int32_t translate_address_read(int32_t address) } else { - return do_page_translation(address, 0, *cpl == 3) | address & 0xFFF; + return do_page_translation(address, false, *cpl == 3) | address & 0xFFF; } } @@ -281,7 +335,37 @@ int32_t translate_address_write(int32_t address) } else { - return do_page_translation(address, 1, *cpl == 3) | address & 0xFFF; + return do_page_translation(address, true, *cpl == 3) | address & 0xFFF; + } +} + +int32_t translate_address_system_read(int32_t address) +{ + if(!*paging) return address; + + int32_t base = (uint32_t)address >> 12; + if(tlb_info[base] & TLB_SYSTEM_READ) + { + return tlb_data[base] ^ address; + } + else + { + return do_page_translation(address, false, false) | address & 0xFFF; + } +} + +int32_t translate_address_system_write(int32_t address) +{ + if(!*paging) return address; + + int32_t base = (uint32_t)address >> 12; + if(tlb_info[base] & TLB_SYSTEM_WRITE) + { + return tlb_data[base] ^ address; + } + else + { + return do_page_translation(address, true, false) | address & 0xFFF; } } @@ -720,6 +804,18 @@ void trigger_nm() raise_exception(7); } +void trigger_np(int32_t code) +{ + *instruction_pointer = *previous_ip; + raise_exception_with_code(11, code); +} + +void trigger_ss(int32_t code) +{ + *instruction_pointer = *previous_ip; + raise_exception_with_code(12, code); +} + void trigger_gp(int32_t code) { *instruction_pointer = *previous_ip; diff --git a/src/native/cpu.h b/src/native/cpu.h index 3cd03a58..be27bde8 100644 --- a/src/native/cpu.h +++ b/src/native/cpu.h @@ -134,3 +134,5 @@ void set_ecx_asize(int32_t value); void add_reg_asize(int32_t reg, int32_t value); int32_t decr_ecx_asize(void); uint64_t read_tsc(void); +bool vm86_mode(void); +int32_t getiopl(void); diff --git a/src/native/js_imports.h b/src/native/js_imports.h index da909095..f863809e 100644 --- a/src/native/js_imports.h +++ b/src/native/js_imports.h @@ -13,7 +13,6 @@ extern bool has_rand_int(void); extern int32_t arpl(int32_t, int32_t); extern int32_t bswap(int32_t); extern int32_t get_rand_int(void); -extern int32_t getiopl(void); extern int32_t int_log2(int32_t); extern int32_t lar(int32_t, int32_t); extern int32_t loop(int32_t); @@ -53,7 +52,6 @@ extern void unimplemented_sse(void); extern void update_cs_size(int32_t); extern void update_eflags(int32_t); extern void switch_seg(int32_t, int32_t); -extern bool vm86_mode(void); extern void lss16(int32_t, int32_t, int32_t); extern void lss32(int32_t, int32_t, int32_t); extern void test_privileges_for_io(int32_t, int32_t); @@ -64,7 +62,6 @@ extern void io_port_write8(int32_t, int32_t); extern void io_port_write16(int32_t, int32_t); extern void io_port_write32(int32_t, int32_t); extern int32_t convert_f64_to_i32(double_t); -extern void call_indirect(int32_t index); extern void jit_clear_func(int32_t index); extern void call_interrupt_vector(int32_t interrupt_nr, bool is_software_int, bool has_error_code, int32_t error_code); extern void throw_cpu_exception(void);