Cleaned up some protected mode features

This commit is contained in:
copy 2014-01-03 22:02:43 +01:00
commit 96d45f16d4
4 changed files with 183 additions and 238 deletions

View file

@ -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"

View file

@ -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
*/

View file

@ -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();

View file

@ -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 : "");