diff --git a/src/cpu.macro.js b/src/cpu.macro.js index 5275adfc..3a6640ae 100644 --- a/src/cpu.macro.js +++ b/src/cpu.macro.js @@ -10,11 +10,13 @@ #define safe_read16s(addr) (safe_read16(addr) << 16 >> 16) #define safe_read32(addr) (safe_read32s(addr) >>> 0) +#define getiopl() (flags >> 12 & 3) + var debug = {}; /** @constructor */ -function v86() +function v86(envapi) { var cpu = this; @@ -35,6 +37,21 @@ this.dev = {}; this.instr_counter = 0; +var next_tick; + +if(envapi.next_tick !== undefined) +{ + next_tick = envapi.next_tick; +} +else +{ + next_tick = function() + { + setTimeout(cpu_run, 0); + }; +} + + var segment_is_null, @@ -202,7 +219,7 @@ var /** @type {Memory} */ memory, - /** @type {(FPU|NoFPU)} */ + /** @type {FPU} */ fpu, /** @@ -444,9 +461,9 @@ function cpu_reboot_internal() function cpu_init(settings) { // see browser/main.js or node/main.js - if(typeof set_tick !== "undefined") + if(typeof envapi.set_tick !== "undefined") { - set_tick(cpu_run); + envapi.set_tick(cpu_run); } current_settings = settings; @@ -491,10 +508,12 @@ function cpu_init(settings) tsr_offset = 0; page_fault = false; - cr0 = 0; + cr0 = 1 << 30 | 1 << 29 | 1 << 4; cr2 = 0; cr3 = 0; cr4 = 0; + dreg[6] = 0xFFFF0FF0; + dreg[7] = 0x400; cpl = 0; paging = false; page_size_extensions = 0; @@ -540,7 +559,8 @@ function cpu_init(settings) time: function() { return performance.now(); }, }; - cpu.dev = {}; + cpu.dev = { + }; devapi.io = cpu.dev.io = io = new IO(memory); @@ -563,12 +583,13 @@ function cpu_init(settings) io.mmap_register(0xFFF00000, 0x100000, 1, function(addr) { - return data[start + addr]; + return memory.mem8[addr]; + //return data[start + addr]; }, function(addr, value) { - data[start + addr] = value; - //memory.mem8[addr] = value; + memory.mem8[addr] = value; + //data[start + addr] = value; }); @@ -661,25 +682,24 @@ function cpu_init(settings) cpu.dev.vga = vga = new VGAScreen(devapi, settings.screen_adapter, VGA_MEMORY_SIZE) cpu.dev.ps2 = ps2 = new PS2(devapi, settings.keyboard_adapter, settings.mouse_adapter); - //fpu = new NoFPU(); fpu = new FPU(devapi); - uart = new UART(devapi); + uart = new UART(devapi, { send_line: envapi.log }); - cpu.dev.fdc = fdc = new FloppyController(devapi, settings.floppy_disk); + cpu.dev.fdc = fdc = new FloppyController(devapi, settings.fda, settings.fdb); - if(settings.cdrom_disk) + if(settings.cdrom) { - cpu.dev.cdrom = cdrom = new IDEDevice(devapi, settings.cdrom_disk, true, 1); + cpu.dev.cdrom = cdrom = new IDEDevice(devapi, settings.cdrom, true, 1); } - if(settings.hda_disk) + if(settings.hda) { - cpu.dev.hda = hda = new IDEDevice(devapi, settings.hda_disk, false, 0); + cpu.dev.hda = hda = new IDEDevice(devapi, settings.hda, false, 0); } - //if(settings.hdb_disk) + //if(settings.hdb) //{ - // cpu.dev.hdb = hdb = new IDEDevice(devapi, settings.hdb_disk, false, 1); + // cpu.dev.hdb = hdb = new IDEDevice(devapi, settings.hdb, false, 1); //} devapi.pit = timer = new PIT(devapi); @@ -902,16 +922,14 @@ function cr0_changed() //protected_mode = (cr0 & 1) === 1; //dbg_log("cr0 = " + h(cr0)); - var new_paging = (cr0 & 0x80000000) !== 0; + var new_paging = (cr0 & (1 << 31)) !== 0; - if(fpu.is_fpu) - { - cr0 &= ~4; - } - else + if(fpu === undefined) { + // if there's no FPU, keep emulation set cr0 |= 4; } + cr0 |= 0x10; if(new_paging !== paging) { @@ -1012,10 +1030,12 @@ var pe_functions = eip_phys = translate_address_read(instruction_pointer) ^ instruction_pointer; last_virt_eip = instruction_pointer & ~0xFFF; } + var data8 = memory.mem8[eip_phys ^ instruction_pointer]; + instruction_pointer = instruction_pointer + 1 | 0; // memory.read8 inlined under the assumption that code never runs in // memory-mapped io - return memory.mem8[eip_phys ^ instruction_pointer++]; + return data8; }, read_imm8s : function() @@ -1026,7 +1046,10 @@ var pe_functions = last_virt_eip = instruction_pointer & ~0xFFF; } - return memory.mem8s[eip_phys ^ instruction_pointer++]; + var data8 = memory.mem8s[eip_phys ^ instruction_pointer]; + instruction_pointer = instruction_pointer + 1 | 0; + + return data8; }, read_imm16 : function() @@ -1273,7 +1296,7 @@ function get_esp_pe_write(mod) */ function get_real_ip() { - return instruction_pointer - get_seg(reg_cs); + return instruction_pointer - get_seg(reg_cs) | 0; } function call_interrupt_vector(interrupt_nr, is_software_int, error_code) @@ -1323,19 +1346,27 @@ function call_interrupt_vector(interrupt_nr, is_software_int, error_code) //if(interrupt_nr === 14) //{ - // dbg_log("int14 error_code=" + error_code + " cr2=" + h(cr2 >>> 0) + " prev=" + h(previous_ip >>> 0) + " cpl=" + cpl, LOG_CPU); + // dbg_log("int14 error_code=" + error_code + + // " cr2=" + h(cr2 >>> 0) + + // " prev=" + h(previous_ip >>> 0) + + // " cpl=" + cpl, LOG_CPU); //} if(in_hlt) { // return to the instruction following the hlt - instruction_pointer++; + instruction_pointer = instruction_pointer + 1 | 0; in_hlt = false; } if(protected_mode) { + if(vm86_mode && (cr4 & 1)) + { + throw unimpl("VME"); + } + if(vm86_mode && is_software_int && getiopl() < 3) { trigger_gp(0); @@ -1580,7 +1611,8 @@ function call_interrupt_vector(interrupt_nr, is_software_int, error_code) // call 4 byte cs:ip interrupt vector from ivt at memory 0 //logop(instruction_pointer, "callu " + h(interrupt_nr) + "." + h(memory.read8(ah))); - //dbg_log("callu " + h(interrupt_nr) + "." + h(memory.read8(ah)) + " at " + h(instruction_pointer, 8), LOG_CPU, LOG_CPU); + //dbg_log("callu " + h(interrupt_nr) + "." + + // h(memory.read8(ah)) + " at " + h(instruction_pointer, 8), LOG_CPU, LOG_CPU); // push flags, cs:ip load_flags(); @@ -1638,6 +1670,12 @@ function trigger_ud() raise_exception(6); } +function trigger_nm() +{ + instruction_pointer = previous_ip; + raise_exception(7); +} + function trigger_gp(code) { instruction_pointer = previous_ip; @@ -1656,7 +1694,6 @@ function trigger_ss(code) raise_exception_with_code(12, code); } - /** * @param {number} seg */ @@ -1746,14 +1783,6 @@ function handle_irqs() } } -/** - * returns the current iopl from the eflags register - */ -function getiopl() -{ - return flags >> 12 & 3; -} - function test_privileges_for_io(port, size) { if(protected_mode && (cpl > getiopl() || (flags & flag_vm))) @@ -1797,9 +1826,9 @@ function cpuid() if(id === 0) { - reg32[reg_ebx] = 0x756E6547; // Genu - reg32[reg_edx] = 0x49656E69; // ineI - reg32[reg_ecx] = 0x6C65746E; // ntel + reg32[reg_ebx] = 0x756E6547|0; // Genu + reg32[reg_edx] = 0x49656E69|0; // ineI + reg32[reg_ecx] = 0x6C65746E|0; // ntel } } else if(id === 1) @@ -1808,12 +1837,12 @@ function cpuid() reg32[reg_eax] = 0x513; reg32[reg_ebx] = 0; reg32[reg_ecx] = 0; - reg32[reg_edx] = fpu.is_fpu | 1 << 3 | 1 << 4 | 1 << 8 | 1 << 13 | 1 << 15; + reg32[reg_edx] = fpu !== undefined | 1 << 3 | 1 << 4 | 1 << 8 | 1 << 13 | 1 << 15; } else if(id === 2) { // Taken from http://siyobik.info.gf/main/reference/instruction/CPUID - reg32[reg_eax] = 0x665B5001; + reg32[reg_eax] = 0x665B5001|0; reg32[reg_ebx] = 0; reg32[reg_ecx] = 0; reg32[reg_edx] = 0x007A7000; @@ -1825,7 +1854,7 @@ function cpuid() reg32[reg_ecx] = 0; reg32[reg_edx] = 0; } - else if((id & 0xF0000000) === ~~0x40000000) + else if((id & (0xF0000000|0)) === 0x40000000) { // Invalid } @@ -1840,47 +1869,39 @@ function cpuid() */ function update_flags(new_flags) { - var oldflags = flags; + var mask = flag_rf | flag_vm | flag_vip | flag_vif, + clear = ~flag_vip & ~flag_vif & flags_mask; if(flags & flag_vm) { - if(getiopl() === 3) - { - // cannot update iopl, vip, vif - flags = (new_flags & ~flag_iopl & ~flag_vip & ~flag_vif) | (flags & (flag_iopl | flag_vip | flag_vif)); - } - else - { - trigger_gp(0); - } + // other case needs to be handled in popf or iret + dbg_assert(getiopl() === 3); + + mask |= flag_iopl; + + // vip and vif are preserved + clear |= flag_vip | flag_vif; } else { - if(cpl === 0 || !protected_mode) - { - // can update all flags - flags = new_flags; - } - else if(cpl <= getiopl()) - { - // cpl != 0 and iopl <= cpl - // can update interrupt flag but not iopl - flags = (new_flags & ~flag_iopl) | (flags & flag_iopl); - } - else - { - // cannot update interrupt flag or iopl - flags = (new_flags & ~flag_iopl & ~flag_interrupt) | (flags & (flag_iopl | flag_interrupt)); - } + if(!protected_mode) dbg_assert(cpl === 0); - // vip and vif are cleared - flags &= ~flag_vip & ~flag_vif; + if(cpl) + { + // cpl > 0 + // cannot update iopl + mask |= flag_iopl; + + if(cpl > getiopl()) + { + // cpl > iopl + // can update interrupt flag but not iopl + mask |= flag_interrupt; + } + } } - // cannot modify rf or vm here - flags = (flags & ~flag_vm & ~flag_rf) | (oldflags & (flag_vm | flag_rf)); - - flags = (flags & flags_mask) | flags_default; + flags = (new_flags ^ ((flags ^ new_flags) & mask)) & clear | flags_default; flags_changed = 0; } @@ -2505,13 +2526,10 @@ function do_page_translation(addr, for_writing, user) function trigger_pagefault(write, user, present) { - if(LOG_LEVEL & LOG_CPU) - { - dbg_log("page fault w=" + write + " u=" + user + " p=" + present + - " eip=" + h(previous_ip >>> 0, 8) + - " cr2=" + h(cr2 >>> 0, 8), LOG_CPU); - //dbg_trace(LOG_CPU); - } + //dbg_log("page fault w=" + write + " u=" + user + " p=" + present + + // " eip=" + h(previous_ip >>> 0, 8) + + // " cr2=" + h(cr2 >>> 0, 8), LOG_CPU); + //dbg_trace(LOG_CPU); // likely invalid pointer reference //if((cr2 >>> 0) < 0x100) @@ -2539,9 +2557,8 @@ function trigger_pagefault(write, user, present) } -// it looks pointless to have these two here, but +// it looks pointless to have this here, but // Closure Compiler is able to remove unused functions -//#include "test_helpers.js" #include "debug.macro.js" diff --git a/src/fpu.macro.js b/src/fpu.macro.js index 9597fa55..aa8ddc3a 100644 --- a/src/fpu.macro.js +++ b/src/fpu.macro.js @@ -3,144 +3,6 @@ /** @const */ var FPU_LOG_OP = false; - -/** - * this behaves as if no x87 fpu existed - * @constructor - */ -function NoFPU(io) -{ - this.is_fpu = 0; - //cr0 |= 4; - - this.fwait = function() - { - - }; - - this.op_D8_reg = function(imm8) - { - trigger_ud(); - }; - - this.op_D8_mem = function(imm8, addr) - { - trigger_ud(); - }; - - this.op_D9_reg = function(imm8) - { - trigger_ud(); - }; - - this.op_D9_mem = function(imm8, addr) - { - var mod = imm8 >> 3 & 7; - - if(mod === 7) - { - // FNSTCW - dbg_log("Unimplemented D9", LOG_FPU); - safe_write16(addr, 0); - } - else - { - trigger_ud(); - } - }; - - this.op_DA = function(imm8) - { - trigger_ud(); - }; - - this.op_DA_mem = function(imm8, addr) - { - trigger_ud(); - }; - - this.op_DB_reg = function(imm8) - { - if(imm8 === 0xE3) - { - // fninit - // don't error, even if no fpu is present - dbg_log("Unimplemented DB", LOG_FPU); - } - else - { - trigger_ud(); - } - }; - - this.op_DB_mem = function(imm8, addr) - { - trigger_ud(); - }; - - this.op_DC_reg = function(imm8) - { - trigger_ud(); - }; - - this.op_DC_mem = function(imm8, addr) - { - trigger_ud(); - }; - - this.op_DD_reg = function(imm8) - { - trigger_ud(); - }; - - this.op_DD_mem = function(imm8, addr) - { - var mod = imm8 >> 3 & 7; - - switch(mod) - { - case 7: - // fnstsw / store status word - // no fpu -> write nonzero - dbg_log("Unimplemented DD", LOG_FPU); - safe_write16(addr, 1); - break; - default: - trigger_ud(); - } - }; - - this.op_DE_reg = function(imm8) - { - trigger_ud(); - }; - - this.op_DE_mem = function(imm8, addr) - { - trigger_ud(); - }; - - this.op_DF_reg = function(imm8) - { - if(imm8 === 0xE0) - { - // fnstsw - // no fpu -> write nonzero - dbg_log("Unimplemented DF", LOG_FPU); - reg16[reg_ax] = 1; - } - else - { - trigger_ud(); - } - }; - - this.op_DF_mem = function(imm8, addr) - { - trigger_ud(); - }; -} - /** * @constructor */ diff --git a/src/instructions.macro.js b/src/instructions.macro.js index 890a4892..be7d28f3 100644 --- a/src/instructions.macro.js +++ b/src/instructions.macro.js @@ -624,11 +624,27 @@ op2(0x9A, { op(0x9B, { // fwait: check for pending fpu exceptions - fpu.fwait(); + if((cr0 & 9) === 9) + { + // task switched and MP bit is set + trigger_nm(); + } + else + { + if(fpu) + { + fpu.fwait(); + } + else + { + // EM bit isn't checked + // If there's no FPU, do nothing + } + } }); op2(0x9C, { // pushf - if((flags & flag_vm) && getiopl() < 3) + if(vm86_mode && getiopl() < 3) { trigger_gp(0); } @@ -639,7 +655,7 @@ op2(0x9C, { } }, { // pushf - if((flags & flag_vm) && getiopl() < 3) + if(vm86_mode && getiopl() < 3) { // trap to virtual 8086 monitor trigger_gp(0); @@ -653,15 +669,21 @@ op2(0x9C, { }); op2(0x9D, { // popf - var tmp; - safe_pop16(tmp); - update_flags((flags & 0xFFFF0000) | tmp); + if(vm86_mode && getiopl() < 3) + { + trigger_gp(0); + } + update_flags((flags & ~0xFFFF) | pop16()); handle_irqs(); }, { // popf - update_flags(pop32s()); + if(vm86_mode && getiopl() < 3) + { + trigger_gp(0); + } + update_flags(pop32s()); handle_irqs(); }); op(0x9E, { @@ -1161,6 +1183,9 @@ op(0xD7, { // fpu instructions #define fpu_op(n, op)\ opm(n, { \ + if(cr0 & 0xC)\ + /* task switch or emulation */\ + trigger_nm();\ if(modrm_byte < 0xC0)\ fpu.op_ ## op ## _mem(modrm_byte, modrm_resolve(modrm_byte));\ else\ @@ -1320,7 +1345,7 @@ op(0xF4, { // hlt if((flags & flag_interrupt) === 0) { - log("cpu halted"); + envapi.log("cpu halted"); stopped = true; if(DEBUG) dump_regs(); throw "HALT"; @@ -1399,7 +1424,16 @@ op(0xFA, { } else { - trigger_gp(0); + if(getiopl() < 3 && (vm86_mode ? + (cr4 & 1) : + (cpl === 3 && (cr4 & 2)))) + { + flags &= ~flag_vif; + } + else + { + trigger_gp(0); + } } }); op(0xFB, { @@ -1417,7 +1451,16 @@ op(0xFB, { } else { - trigger_gp(0); + if(getiopl() < 3 && (flags & flag_vip) === 0 && (vm86_mode ? + (cr4 & 1) : + (cpl === 3 && (cr4 & 2)))) + { + flags |= flag_vif; + } + else + { + trigger_gp(0); + } } }); @@ -1620,6 +1663,14 @@ opm(0x01, { read_e16; cr0 = (cr0 & ~0xF) | (data & 0xF); + + if(protected_mode) + { + // lmsw cannot be used to switch back + cr0 |= 1; + } + + //dbg_log("cr0=" + h(data >>> 0), LOG_CPU); cr0_changed(); return; } @@ -1848,12 +1899,14 @@ opm(0x22, { cr0 = data; cr0_changed(); - //dbg_log("cr1 = " + bits(memory.read32s(addr)), LOG_CPU); + //dbg_log("cr0=" + h(data >>> 0), LOG_CPU); break; + case 2: cr2 = data; - dbg_log("cr2 <- " + h(data >>> 0), LOG_CPU); + //dbg_log("cr2=" + h(data >>> 0), LOG_CPU); break; + case 3: cr3 = data; dbg_assert((cr3 & 0xFFF) === 0); @@ -1862,7 +1915,13 @@ opm(0x22, { //dump_page_directory(); //dbg_log("page directory loaded at " + h(cr3 >>> 0, 8), LOG_CPU); break; + case 4: + if(data & (1 << 11 | 1 << 12 | 1 << 15 | 1 << 16 | 1 << 19 | 0xFFC00000)) + { + trigger_gp(0); + } + if((cr4 ^ data) & 0x80) { full_clear_tlb(); @@ -1870,9 +1929,15 @@ opm(0x22, { cr4 = data; page_size_extensions = (cr4 & 16) ? PSE_ENABLED : 0; - //dbg_log("cr4 set to " + h(cr4 >>> 0), LOG_CPU); - + + if(cr4 & 0x20) + { + throw unimpl("PAE"); + } + + dbg_log("cr4=" + h(cr4 >>> 0), LOG_CPU); break; + default: dbg_log(modrm_byte >> 3 & 7); todo(); diff --git a/src/main.js b/src/main.js index 49ac7880..e3cfa2e8 100644 --- a/src/main.js +++ b/src/main.js @@ -109,6 +109,7 @@ function dbg_assert(cond, msg, level) //dump_regs(); console.log(Error().stack); console.trace(); + if(msg) { throw "Assert failed: " + msg; @@ -238,7 +239,7 @@ Math.bcd_pack = function(n) /** * @param {string=} msg - * */ + */ function unimpl(msg) { var s = "Unimplemented" + (msg ? ": " + msg : "");