2013-11-07 21:30:18 +01:00
|
|
|
"use strict";
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
#define getiopl(f) (f >> 12 & 3)
|
|
|
|
#define logop(x, y) if(DEBUG) { this.debug.logop(x, y); }
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-05-14 00:26:04 +02:00
|
|
|
if(typeof window === "object")
|
|
|
|
{
|
|
|
|
window["v86"] = v86;
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/** @constructor */
|
2014-06-15 22:25:17 +02:00
|
|
|
function v86()
|
2014-01-03 22:02:43 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
var cpu = this;
|
2014-01-03 22:02:43 +01:00
|
|
|
|
2014-01-08 03:22:30 +01:00
|
|
|
/** @type {number } */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory_size = 0;
|
2014-01-08 03:22:30 +01:00
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.segment_is_null = [];
|
|
|
|
this.segment_offsets = [];
|
|
|
|
this.segment_limits = [];
|
|
|
|
this.segment_infos = [];
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Translation Lookaside Buffer
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_user_read = [];
|
|
|
|
this.tlb_user_write = [];
|
|
|
|
this.tlb_system_read = [];
|
|
|
|
this.tlb_system_write = [];
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Information about which pages are cached in the tlb.
|
|
|
|
* By bit:
|
|
|
|
* 0 system, read
|
|
|
|
* 1 system, write
|
|
|
|
* 2 user, read
|
|
|
|
* 3 user, write
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_info = [];
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Same as tlb_info, except it only contains global pages
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_info_global = [];
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Wheter or not in protected mode
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.protected_mode = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* interrupt descriptor table
|
|
|
|
* @type {number}
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.idtr_size = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.idtr_offset = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* global descriptor table register
|
|
|
|
* @type {number}
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.gdtr_size = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.gdtr_offset = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* local desciptor table
|
|
|
|
* @type {number}
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.ldtr_size = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.ldtr_offset = 0;
|
2014-02-11 21:42:28 +01:00
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.ldtr_selector = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* task register
|
|
|
|
* @type {number}
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tsr_size = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tsr_offset = 0;
|
2014-02-11 21:42:28 +01:00
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tsr_selector = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* whether or not a page fault occured
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.page_fault = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cr0 = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cr2 = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cr3 = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cr4 = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// current privilege level
|
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cpl = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// if false, pages are 4 KiB, else 4 Mib
|
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.page_size_extensions = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// current operand/address/stack size
|
|
|
|
/** @type {boolean} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.is_32 = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {boolean} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.operand_size_32 = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {boolean} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.stack_size_32 = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {boolean} */
|
|
|
|
this.address_size_32 = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
2014-06-15 22:25:17 +02:00
|
|
|
* Was the last instruction a hlt?
|
2013-11-07 21:30:18 +01:00
|
|
|
* @type {boolean}
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.in_hlt = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {boolean} */
|
|
|
|
this.running = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {boolean} */
|
|
|
|
this.stopped = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.devices = {};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.last_virt_eip = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.eip_phys = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.last_virt_esp = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.esp_phys = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.repeat_string_prefix = REPEAT_STRING_PREFIX_NONE;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.flags = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/**
|
|
|
|
* bitmap of flags which are not updated in the flags variable
|
|
|
|
* changed by arithmetic instructions, so only relevant to arithmetic flags
|
|
|
|
* @type {number}
|
2013-11-07 21:30:18 +01:00
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.flags_changed = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/**
|
|
|
|
* the last 2 operators and the result and size of the last arithmetic operation
|
|
|
|
* @type {number}
|
2013-11-07 21:30:18 +01:00
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.last_op1 = 0;
|
|
|
|
/** @type {number} */
|
|
|
|
this.last_op2 = 0;
|
|
|
|
/** @type {number} */
|
|
|
|
this.last_op_size = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.last_add_result = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.last_result = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// cpu.reg16 or cpu.reg32s, depending on address size attribute
|
|
|
|
this.regv = this.reg16;
|
|
|
|
this.reg_vcx = 0;
|
|
|
|
this.reg_vsi = 0;
|
|
|
|
this.reg_vdi = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.table = [];
|
|
|
|
this.table0F = [];
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.current_settings = {};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// paging enabled
|
|
|
|
/** @type {boolean} */
|
|
|
|
this.paging = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.previous_ip = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/**
|
|
|
|
* Cycles since last cpu reset, used by rdtsc instruction
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
this.timestamp_counter = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
//this.modrm_resolve = function(x){ dbg_assert(false); };
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// registers
|
|
|
|
this.reg32s = new Int32Array(8);
|
|
|
|
this.reg32 = new Uint32Array(this.reg32s.buffer);
|
|
|
|
this.reg16s = new Int16Array(this.reg32s.buffer);
|
|
|
|
this.reg16 = new Uint16Array(this.reg32s.buffer);
|
|
|
|
this.reg8s = new Int8Array(this.reg32s.buffer);
|
|
|
|
this.reg8 = new Uint8Array(this.reg32s.buffer);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// segment registers
|
|
|
|
this.sreg = new Uint16Array(8);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// debug registers
|
|
|
|
this.dreg = new Int32Array(8);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// sp or esp, depending on stack size attribute
|
|
|
|
this.stack_reg = this.reg16;
|
|
|
|
this.reg_vsp = 0;
|
|
|
|
this.reg_vbp = 0;
|
2014-01-05 03:17:40 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {Memory} */
|
|
|
|
this.memory = null;
|
2014-01-05 03:17:40 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// current state of prefixes
|
|
|
|
this.segment_prefix = SEG_PREFIX_NONE;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// dynamic instruction translator
|
|
|
|
this.translator = undefined;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// was the last instruction a jump?
|
|
|
|
this.last_instr_jump = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.first_init = true;
|
|
|
|
this.next_tick = function() {};
|
|
|
|
this.microtick = function() {};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.io = undefined;
|
|
|
|
this.fpu = undefined;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// it looks pointless to have this here, but
|
|
|
|
// Closure Compiler is able to remove unused functions
|
|
|
|
#include "debug.macro.js"
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
Object.preventExtensions(this);
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
#include "translate.macro.js"
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
#include "modrm.macro.js"
|
|
|
|
#include "arith.macro.js"
|
|
|
|
#include "string.macro.js"
|
|
|
|
#include "instructions.macro.js"
|
|
|
|
#include "misc_instr.macro.js"
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-17 01:38:39 +02:00
|
|
|
#undef unimpl
|
2014-06-15 22:25:17 +02:00
|
|
|
#define unimpl(x) this.debug.unimpl(x)
|
|
|
|
#define vm86_mode() (!!(this.flags & flag_vm))
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.run = function()
|
|
|
|
{
|
|
|
|
if(!this.running)
|
|
|
|
{
|
|
|
|
this.next_tick();
|
|
|
|
}
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.main_run = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.stopped)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.stopped = this.running = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.running = true;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
try {
|
2014-06-22 19:10:35 +02:00
|
|
|
if(this.in_hlt)
|
|
|
|
{
|
|
|
|
this.hlt_loop();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.do_run();
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
catch(e)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.exception_cleanup(e);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
v86.prototype.stop = function()
|
|
|
|
{
|
|
|
|
if(this.running)
|
|
|
|
{
|
|
|
|
this.stopped = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
v86.prototype.restart = function()
|
|
|
|
{
|
|
|
|
dbg_log("cpu restart", LOG_CPU);
|
|
|
|
|
|
|
|
var was_running = this.running;
|
|
|
|
var cpu = this;
|
|
|
|
|
|
|
|
this.stopped = true;
|
|
|
|
this.running = false;
|
2013-12-06 20:47:37 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
setTimeout(function()
|
|
|
|
{
|
|
|
|
cpu.devices.ps2.destroy();
|
|
|
|
cpu.devices.vga.destroy();
|
|
|
|
|
|
|
|
cpu.init(cpu.current_settings);
|
|
|
|
|
|
|
|
if(was_running)
|
|
|
|
{
|
|
|
|
cpu.next_tick();
|
|
|
|
}
|
|
|
|
}, 10);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
v86.prototype.exception_cleanup = function(e)
|
2013-12-06 20:47:37 +01:00
|
|
|
{
|
2014-05-06 20:24:46 +02:00
|
|
|
if(e === MAGIC_CPU_EXCEPTION)
|
2013-12-06 20:47:37 +01:00
|
|
|
{
|
|
|
|
// A legit CPU exception (for instance, a page fault happened)
|
|
|
|
// call_interrupt_vector has already been called at this point,
|
|
|
|
// so we just need to reset some state
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.page_fault = false;
|
2013-12-06 20:47:37 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// restore state from prefixes
|
|
|
|
this.repeat_string_prefix = REPEAT_STRING_PREFIX_NONE;
|
|
|
|
this.segment_prefix = SEG_PREFIX_NONE;
|
|
|
|
|
|
|
|
this.address_size_32 = this.is_32;
|
|
|
|
this.update_address_size();
|
|
|
|
this.operand_size_32 = this.is_32;
|
|
|
|
this.update_operand_size();
|
2013-12-06 20:47:37 +01:00
|
|
|
|
2014-06-24 16:10:40 +02:00
|
|
|
if(!DEBUG || !this.debug.step_mode)
|
|
|
|
{
|
|
|
|
this.next_tick();
|
|
|
|
}
|
2013-12-06 20:47:37 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-16 20:56:42 +02:00
|
|
|
this.running = false;
|
|
|
|
|
2013-12-06 20:47:37 +01:00
|
|
|
console.log(e);
|
|
|
|
console.log(e.stack);
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.reboot_internal = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
dbg_assert(this.running);
|
2014-05-12 18:44:28 +02:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.devices.ps2.destroy();
|
|
|
|
this.devices.vga.destroy();
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.init(this.current_settings);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
throw MAGIC_CPU_EXCEPTION;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// initialization that only needs to be once
|
|
|
|
v86.prototype.lazy_init = function()
|
|
|
|
{
|
|
|
|
var cpu = this;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(typeof setImmediate !== "undefined")
|
|
|
|
{
|
|
|
|
this.next_tick = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
setImmediate(function() { cpu.main_run(); });
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else if(typeof window !== "undefined" && typeof postMessage !== "undefined")
|
|
|
|
{
|
|
|
|
// setImmediate shim for the browser.
|
|
|
|
// TODO: Make this deactivatable, for other applications
|
|
|
|
// using postMessage
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @const */
|
|
|
|
var MAGIC_POST_MESSAGE = 0xAA55;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
window.addEventListener("message", function(e)
|
|
|
|
{
|
|
|
|
if(e.source === window && e.data === MAGIC_POST_MESSAGE)
|
|
|
|
{
|
|
|
|
cpu.main_run();
|
|
|
|
}
|
|
|
|
}, false);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.next_tick = function()
|
|
|
|
{
|
|
|
|
window.postMessage(MAGIC_POST_MESSAGE, "*");
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.next_tick = function()
|
|
|
|
{
|
|
|
|
setTimeout(this.main_run, 0);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if(typeof performance === "object" && performance.now)
|
|
|
|
{
|
|
|
|
this.microtick = function()
|
|
|
|
{
|
|
|
|
return performance.now();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.microtick = Date.now;
|
|
|
|
}
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.init = function(settings)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.first_init)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.first_init = false;
|
|
|
|
this.lazy_init();
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.current_settings = settings;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory_size = settings.memory_size || 1024 * 1024 * 64;
|
2014-01-08 03:22:30 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory = new Memory(new ArrayBuffer(this.memory_size), this.memory_size);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.segment_is_null = new Uint8Array(8);
|
|
|
|
this.segment_limits = new Uint32Array(8);
|
|
|
|
this.segment_infos = new Uint32Array(8);
|
|
|
|
this.segment_offsets = new Int32Array(8);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// 16 MB in total
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_user_read = new Int32Array(1 << 20);
|
|
|
|
this.tlb_user_write = new Int32Array(1 << 20);
|
|
|
|
this.tlb_system_read = new Int32Array(1 << 20);
|
|
|
|
this.tlb_system_write = new Int32Array(1 << 20);
|
|
|
|
|
|
|
|
this.tlb_info = new Uint8Array(1 << 20);
|
|
|
|
this.tlb_info_global = new Uint8Array(1 << 20);
|
|
|
|
|
|
|
|
|
|
|
|
this.reg32 = new Uint32Array(8);
|
|
|
|
this.reg32s = new Int32Array(this.reg32.buffer);
|
|
|
|
this.reg16 = new Uint16Array(this.reg32.buffer);
|
|
|
|
this.reg16s = new Int16Array(this.reg32.buffer);
|
|
|
|
this.reg8 = new Uint8Array(this.reg32.buffer);
|
|
|
|
this.reg8s = new Int8Array(this.reg32.buffer);
|
|
|
|
this.sreg = new Uint16Array(8);
|
|
|
|
this.dreg = new Int32Array(8);
|
|
|
|
this.protected_mode = false;
|
|
|
|
|
|
|
|
// http://www.sandpile.org/x86/initial.htm
|
|
|
|
this.idtr_size = 0;
|
|
|
|
this.idtr_offset = 0;
|
|
|
|
|
|
|
|
this.gdtr_size = 0;
|
|
|
|
this.gdtr_offset = 0;
|
|
|
|
|
|
|
|
this.ldtr_size = 0;
|
|
|
|
this.ldtr_offset = 0;
|
|
|
|
this.ldtr_selector = 0;
|
|
|
|
|
|
|
|
this.tsr_size = 0;
|
|
|
|
this.tsr_offset = 0;
|
|
|
|
this.tsr_selector = 0;
|
|
|
|
|
|
|
|
this.page_fault = false;
|
|
|
|
this.cr0 = 1 << 30 | 1 << 29 | 1 << 4;
|
|
|
|
this.cr2 = 0;
|
|
|
|
this.cr3 = 0;
|
|
|
|
this.cr4 = 0;
|
|
|
|
this.dreg[6] = 0xFFFF0FF0|0;
|
|
|
|
this.dreg[7] = 0x400;
|
|
|
|
this.cpl = 0;
|
|
|
|
this.paging = false;
|
|
|
|
this.page_size_extensions = 0;
|
|
|
|
this.is_32 = false;
|
|
|
|
this.operand_size_32 = false;
|
|
|
|
this.stack_size_32 = false;
|
|
|
|
this.address_size_32 = false;
|
|
|
|
|
|
|
|
this.paging_changed();
|
|
|
|
|
|
|
|
this.update_operand_size();
|
|
|
|
this.update_address_size();
|
|
|
|
|
|
|
|
this.stack_reg = this.reg16;
|
|
|
|
this.reg_vsp = reg_sp;
|
|
|
|
this.reg_vbp = reg_bp;
|
|
|
|
|
|
|
|
this.timestamp_counter = 0;
|
|
|
|
this.previous_ip = 0;
|
|
|
|
this.in_hlt = false;
|
|
|
|
|
|
|
|
this.running = false;
|
|
|
|
this.stopped = false;
|
|
|
|
|
|
|
|
this.segment_prefix = SEG_PREFIX_NONE;
|
|
|
|
this.repeat_string_prefix = REPEAT_STRING_PREFIX_NONE;
|
|
|
|
this.flags = flags_default;
|
|
|
|
this.flags_changed = 0;
|
|
|
|
|
|
|
|
this.last_result = 0;
|
|
|
|
this.last_add_result = 0;
|
|
|
|
this.last_op1 = 0;
|
|
|
|
this.last_op2 = 0;
|
|
|
|
this.last_op_size = 0;
|
|
|
|
|
|
|
|
if(OP_TRANSLATION)
|
|
|
|
{
|
|
|
|
this.translator = new DynamicTranslator(this);
|
|
|
|
this.last_instr_jump = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
var io = new IO(this.memory);
|
|
|
|
this.io = this.devices.io = io;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(settings.bios)
|
|
|
|
{
|
|
|
|
// load bios
|
|
|
|
var data = new Uint8Array(settings.bios),
|
|
|
|
start = 0x100000 - settings.bios.byteLength;
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.mem8.set(data, start);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(settings.vga_bios)
|
|
|
|
{
|
|
|
|
// load vga bios
|
|
|
|
data = new Uint8Array(settings.vga_bios);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.mem8.set(data, 0xC0000);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2013-11-08 18:10:11 +01:00
|
|
|
// seabios expects the bios to be mapped to 0xFFF00000 also
|
2013-12-20 22:04:58 +01:00
|
|
|
io.mmap_register(0xFFF00000, 0x100000, 1,
|
2013-11-08 18:10:11 +01:00
|
|
|
function(addr)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.memory.mem8[addr];
|
2014-01-03 22:02:43 +01:00
|
|
|
//return data[start + addr];
|
2014-06-24 16:10:40 +02:00
|
|
|
}.bind(this),
|
2013-11-08 18:10:11 +01:00
|
|
|
function(addr, value)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.mem8[addr] = value;
|
2014-01-03 22:02:43 +01:00
|
|
|
//data[start + addr] = value;
|
2014-06-24 16:10:40 +02:00
|
|
|
}.bind(this));
|
2013-11-08 18:10:11 +01:00
|
|
|
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
// ip initial value
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = 0xFFFF0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// ss and sp inital value
|
2014-06-15 22:25:17 +02:00
|
|
|
this.switch_seg(reg_ss, 0x30);
|
|
|
|
this.reg16[reg_sp] = 0x100;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else if(settings.linux)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = 0x10000;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.write_blob(new Uint8Array(settings.linux.vmlinux), 0x100000);
|
|
|
|
this.memory.write_blob(new Uint8Array(settings.linux.linuxstart), this.instruction_pointer);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(settings.linux.root)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.write_blob(new Uint8Array(settings.linux.root), 0x00400000);
|
|
|
|
this.reg32s[reg_ebx] = settings.linux.root.byteLength;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.write_string(settings.linux.cmdline, 0xF800);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.reg32s[reg_eax] = this.memory_size;
|
|
|
|
this.reg32s[reg_ecx] = 0xF800;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.switch_seg(reg_cs, 0);
|
|
|
|
this.switch_seg(reg_ss, 0);
|
|
|
|
this.switch_seg(reg_ds, 0);
|
|
|
|
this.switch_seg(reg_es, 0);
|
|
|
|
this.switch_seg(reg_gs, 0);
|
|
|
|
this.switch_seg(reg_fs, 0);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.is_32 = true;
|
|
|
|
this.address_size_32 = true;
|
|
|
|
this.operand_size_32 = true;
|
|
|
|
this.stack_size_32 = true;
|
|
|
|
this.protected_mode = true;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.update_operand_size();
|
|
|
|
this.update_address_size();
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.regv = this.reg32s;
|
|
|
|
this.reg_vsp = reg_esp;
|
|
|
|
this.reg_vbp = reg_ebp;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cr0 = 1;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.switch_seg(reg_ss, 0x30);
|
|
|
|
this.reg16[reg_sp] = 0x100;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-12-20 22:04:58 +01:00
|
|
|
var a20_byte = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2013-12-20 22:04:58 +01:00
|
|
|
io.register_read(0x92, function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2013-12-20 22:04:58 +01:00
|
|
|
return a20_byte;
|
|
|
|
});
|
|
|
|
|
|
|
|
io.register_write(0x92, function(out_byte)
|
|
|
|
{
|
|
|
|
a20_byte = out_byte;
|
|
|
|
});
|
|
|
|
|
|
|
|
if(DEBUG)
|
|
|
|
{
|
|
|
|
// Use by linux for port-IO delay
|
|
|
|
// Avoid generating tons of debug messages
|
|
|
|
io.register_write(0x80, function(out_byte)
|
|
|
|
{
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2013-12-20 22:04:58 +01:00
|
|
|
// TODO: Make this more configurable
|
|
|
|
if(settings.load_devices)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
var devices = {};
|
|
|
|
this.devices = devices;
|
|
|
|
|
|
|
|
devices.pic = new PIC(this);
|
|
|
|
|
|
|
|
devices.pci = new PCI(this);
|
|
|
|
devices.dma = new DMA(this);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
devices.acpi = new ACPI(this);
|
|
|
|
if(ENABLE_HPET)
|
|
|
|
{
|
|
|
|
devices.hpet = new HPET(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.devices.vga = new VGAScreen(this,
|
2014-01-08 03:22:30 +01:00
|
|
|
settings.screen_adapter, settings.vga_memory_size || 8 * 1024 * 1024);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.devices.ps2 = new PS2(this, settings.keyboard_adapter, settings.mouse_adapter);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.fpu = new FPU(this);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-01-08 03:22:30 +01:00
|
|
|
if(settings.serial_adapter)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
devices.uart = new UART(this, 0x3F8, settings.serial_adapter);
|
2014-01-08 03:22:30 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
devices.uart = new UART(this, 0x3F8, {
|
|
|
|
//put_line: envapi.log,
|
|
|
|
put_line: function() { },
|
2014-01-08 03:22:30 +01:00
|
|
|
init: function(fn) { },
|
|
|
|
});
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.devices.fdc = new FloppyController(this, settings.fda, settings.fdb);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-01-03 22:02:43 +01:00
|
|
|
if(settings.cdrom)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.devices.cdrom = new IDEDevice(this, settings.cdrom, true, 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-01-03 22:02:43 +01:00
|
|
|
if(settings.hda)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.devices.hda = new IDEDevice(this, settings.hda, false, 0);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-01-10 04:24:56 +01:00
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
//this.devices.hda = new IDEDevice(this, undefined, false, 0);
|
2014-01-10 04:24:56 +01:00
|
|
|
}
|
2014-01-03 22:02:43 +01:00
|
|
|
//if(settings.hdb)
|
2013-11-26 21:58:12 +01:00
|
|
|
//{
|
2014-06-15 22:25:17 +02:00
|
|
|
// this.devices.hdb = hdb = new IDEDevice(this, settings.hdb, false, 1);
|
2013-11-26 21:58:12 +01:00
|
|
|
//}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
devices.pit = new PIT(this);
|
|
|
|
devices.rtc = new RTC(this, devices.fdc.type, settings.boot_order || 0x213);
|
2014-01-02 01:05:49 +01:00
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(DEBUG)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.debug.init();
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.do_run = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
var
|
|
|
|
/**
|
|
|
|
* @type {number}
|
|
|
|
*/
|
2013-12-06 01:34:13 +01:00
|
|
|
start = Date.now(),
|
2014-02-07 17:41:48 +01:00
|
|
|
now = start;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.devices.vga.timer(now);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// outer loop:
|
|
|
|
// runs cycles + timers
|
2014-02-07 17:41:48 +01:00
|
|
|
for(; now - start < TIME_PER_FRAME;)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2013-12-06 01:34:13 +01:00
|
|
|
if(ENABLE_HPET)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.devices.pit.timer(now, this.devices.hpet.legacy_mode);
|
|
|
|
this.devices.rtc.timer(now, this.devices.hpet.legacy_mode);
|
|
|
|
this.devices.hpet.timer(now);
|
2013-12-06 01:34:13 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.devices.pit.timer(now, false);
|
|
|
|
this.devices.rtc.timer(now, false);
|
2013-12-06 01:34:13 +01:00
|
|
|
}
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
// inner loop:
|
|
|
|
// runs only cycles
|
|
|
|
for(var k = LOOP_COUNTER; k--;)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(OP_TRANSLATION)
|
|
|
|
{
|
|
|
|
this.translator.cycle_translated();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.cycle();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
now = Date.now();
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.next_tick();
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
|
|
|
// do_run must not be inlined into cpu_run, because then more code
|
|
|
|
// is in the deoptimized try-catch.
|
|
|
|
// This trick is a bit ugly, but it works without further complication.
|
|
|
|
if(typeof window !== "undefined")
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
window.__no_inline1 = v86.prototype.do_run;
|
|
|
|
window.__no_inline2 = v86.prototype.exception_cleanup;
|
2014-06-22 19:10:35 +02:00
|
|
|
window.__no_inline3 = v86.prototype.hlt_loop;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* execute a single instruction cycle on the cpu
|
|
|
|
* this includes reading all prefixes and the whole instruction
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.cycle = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.timestamp_counter++;
|
|
|
|
this.previous_ip = this.instruction_pointer;
|
2014-05-12 18:44:28 +02:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var opcode = this.read_imm8();
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
logop(this.instruction_pointer - 1 >>> 0, opcode);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// call the instruction
|
2014-06-15 22:25:17 +02:00
|
|
|
this.table[opcode](this);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.flags & flag_trap)
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
dbg_log("Trap flag: Ignored", LOG_CPU);
|
|
|
|
}
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-22 19:10:35 +02:00
|
|
|
v86.prototype.hlt_loop = function()
|
|
|
|
{
|
2014-06-29 22:28:55 +02:00
|
|
|
//dbg_log("In HLT loop", LOG_CPU);
|
|
|
|
|
2014-06-22 19:10:35 +02:00
|
|
|
var now = Date.now();
|
|
|
|
|
|
|
|
if(ENABLE_HPET)
|
|
|
|
{
|
|
|
|
this.devices.pit.timer(now, this.devices.hpet.legacy_mode);
|
|
|
|
this.devices.rtc.timer(now, this.devices.hpet.legacy_mode);
|
|
|
|
this.devices.hpet.timer(now);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.devices.pit.timer(now, false);
|
|
|
|
this.devices.rtc.timer(now, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.devices.vga.timer(now);
|
|
|
|
|
|
|
|
if(this.in_hlt)
|
|
|
|
{
|
|
|
|
var me = this;
|
|
|
|
setTimeout(function() { me.hlt_loop(); }, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.next_tick();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.cr0_changed = function(old_cr0)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-28 22:59:42 +02:00
|
|
|
//dbg_log("cr0 = " + h(this.cr0 >>> 0), LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-28 22:59:42 +02:00
|
|
|
var new_paging = (this.cr0 & CR0_PG) === CR0_PG;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!this.fpu)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-01-03 22:02:43 +01:00
|
|
|
// if there's no FPU, keep emulation set
|
2014-06-28 22:59:42 +02:00
|
|
|
this.cr0 |= CR0_EM;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-28 22:59:42 +02:00
|
|
|
this.cr0 |= CR0_ET;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
dbg_assert(typeof this.paging === "boolean");
|
|
|
|
if(new_paging !== this.paging)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.paging = new_paging;
|
|
|
|
this.paging_changed();
|
|
|
|
this.full_clear_tlb();
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-05-12 18:44:28 +02:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.translator !== undefined && (this.cr0 ^ old_cr0) & 1)
|
2014-05-12 18:44:28 +02:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.translator.clear_cache();
|
2014-05-12 18:44:28 +02:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.paging_changed = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.last_virt_eip = -1;
|
|
|
|
this.last_virt_esp = -1;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.cpl_changed = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.last_virt_eip = -1;
|
|
|
|
this.last_virt_esp = -1;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.get_phys_eip = function()
|
|
|
|
{
|
|
|
|
if((this.instruction_pointer & ~0xFFF) ^ this.last_virt_eip)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.eip_phys = this.translate_address_read(this.instruction_pointer) ^ this.instruction_pointer;
|
|
|
|
this.last_virt_eip = this.instruction_pointer & ~0xFFF;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.eip_phys ^ this.instruction_pointer;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.read_imm8 = function()
|
|
|
|
{
|
|
|
|
dbg_assert(this.instruction_pointer !== undefined);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if((this.instruction_pointer & ~0xFFF) ^ this.last_virt_eip)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.eip_phys = this.translate_address_read(this.instruction_pointer) ^ this.instruction_pointer;
|
|
|
|
this.last_virt_eip = this.instruction_pointer & ~0xFFF;
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// memory.read8 inlined under the assumption that code never runs in
|
|
|
|
// memory-mapped space
|
|
|
|
var data8 = this.memory.mem8[this.eip_phys ^ this.instruction_pointer];
|
|
|
|
this.instruction_pointer = this.instruction_pointer + 1 | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
return data8;
|
2013-11-07 21:30:18 +01:00
|
|
|
};
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.read_imm8s = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.read_imm8() << 24 >> 24;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.read_imm16 = function()
|
|
|
|
{
|
|
|
|
dbg_assert(this.instruction_pointer !== undefined);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// 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 ^ this.last_virt_eip) >>> 0) > 0xFFE)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.read_imm8() | this.read_imm8() << 8;
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var data16 = this.memory.read16(this.eip_phys ^ this.instruction_pointer);
|
|
|
|
this.instruction_pointer = this.instruction_pointer + 2 | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
return data16;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.read_imm16s = function()
|
|
|
|
{
|
|
|
|
return this.read_imm16() << 16 >> 16;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.read_imm32s = function()
|
|
|
|
{
|
|
|
|
dbg_assert(this.instruction_pointer !== undefined);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// Analogue to the above comment
|
|
|
|
if(((this.instruction_pointer ^ this.last_virt_eip) >>> 0) > 0xFFC)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.read_imm16() | this.read_imm16() << 16;
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var data32 = this.memory.read32s(this.eip_phys ^ this.instruction_pointer);
|
|
|
|
this.instruction_pointer = this.instruction_pointer + 4 | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
return data32;
|
2013-11-07 21:30:18 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// read word from a page boundary, given 2 physical addresses
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.virt_boundary_read16 = function(low, high)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_assert((low & 0xFFF) === 0xFFF);
|
|
|
|
dbg_assert((high & 0xFFF) === 0);
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.memory.read8(low) | this.memory.read8(high) << 8;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// read doubleword from a page boundary, given 2 addresses
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.virt_boundary_read32s = function(low, high)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_assert((low & 0xFFF) >= 0xFFD);
|
|
|
|
dbg_assert((high - 3 & 0xFFF) === (low & 0xFFF));
|
|
|
|
|
2014-01-10 04:24:56 +01:00
|
|
|
var mid;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(low & 1)
|
|
|
|
{
|
|
|
|
if(low & 2)
|
|
|
|
{
|
|
|
|
// 0xFFF
|
2014-06-15 22:25:17 +02:00
|
|
|
mid = this.memory.read_aligned16(high - 2 >> 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// 0xFFD
|
2014-06-15 22:25:17 +02:00
|
|
|
mid = this.memory.read_aligned16(low + 1 >> 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// 0xFFE
|
2014-06-15 22:25:17 +02:00
|
|
|
mid = this.virt_boundary_read16(low + 1, high - 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.memory.read8(low) | mid << 8 | this.memory.read8(high) << 24;;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.virt_boundary_write16 = function(low, high, value)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_assert((low & 0xFFF) === 0xFFF);
|
|
|
|
dbg_assert((high & 0xFFF) === 0);
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.write8(low, value);
|
|
|
|
this.memory.write8(high, value >> 8);
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.virt_boundary_write32 = function(low, high, value)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_assert((low & 0xFFF) >= 0xFFD);
|
|
|
|
dbg_assert((high - 3 & 0xFFF) === (low & 0xFFF));
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.write8(low, value);
|
|
|
|
this.memory.write8(high, value >> 24);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(low & 1)
|
|
|
|
{
|
|
|
|
if(low & 2)
|
|
|
|
{
|
|
|
|
// 0xFFF
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.write8(high - 2, value >> 8);
|
|
|
|
this.memory.write8(high - 1, value >> 16);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// 0xFFD
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.write8(low + 1, value >> 8);
|
|
|
|
this.memory.write8(low + 2, value >> 16);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// 0xFFE
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.write8(low + 1, value >> 8);
|
|
|
|
this.memory.write8(high - 1, value >> 16);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// safe_read, safe_write
|
|
|
|
// read or write byte, word or dword to the given *virtual* address,
|
|
|
|
// and be safe on page boundaries
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.safe_read8 = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-01-02 01:05:49 +01:00
|
|
|
dbg_assert(addr < 0x80000000);
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.memory.read8(this.translate_address_read(addr));
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.safe_read16 = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.paging && (addr & 0xFFF) === 0xFFF)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.safe_read8(addr) | this.safe_read8(addr + 1) << 8;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.memory.read16(this.translate_address_read(addr));
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.safe_read32s = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.paging && (addr & 0xFFF) >= 0xFFD)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.safe_read16(addr) | this.safe_read16(addr + 2) << 16;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.memory.read32s(this.translate_address_read(addr));
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.safe_write8 = function(addr, value)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-01-02 01:05:49 +01:00
|
|
|
dbg_assert(addr < 0x80000000);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.write8(this.translate_address_write(addr), value);
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.safe_write16 = function(addr, value)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
var phys_low = this.translate_address_write(addr);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if((addr & 0xFFF) === 0xFFF)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.virt_boundary_write16(phys_low, this.translate_address_write(addr + 1), value);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.write16(phys_low, value);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.safe_write32 = function(addr, value)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
var phys_low = this.translate_address_write(addr);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if((addr & 0xFFF) >= 0xFFD)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.virt_boundary_write32(phys_low, this.translate_address_write(addr + 3), value);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.write32(phys_low, value);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// read 2 or 4 byte from ip, depending on address size attribute
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.read_moffs = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.address_size_32)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.get_seg_prefix(reg_ds) + this.read_imm32s() | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.get_seg_prefix(reg_ds) + this.read_imm16() | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.get_eflags = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return (this.flags & ~flags_all) | !!this.getcf() | !!this.getpf() << 2 | !!this.getaf() << 4 |
|
|
|
|
!!this.getzf() << 6 | !!this.getsf() << 7 | !!this.getof() << 11;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.load_eflags = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.flags = this.get_eflags();
|
|
|
|
this.flags_changed = 0;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/**
|
|
|
|
* Update the flags register depending on iopl and cpl
|
|
|
|
*/
|
|
|
|
v86.prototype.update_eflags = function(new_flags)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
var mask = flag_rf | flag_vm | flag_vip | flag_vif,
|
|
|
|
clear = ~flag_vip & ~flag_vif & flags_mask;
|
|
|
|
|
|
|
|
if(this.flags & flag_vm)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
// other case needs to be handled in popf or iret
|
|
|
|
dbg_assert(getiopl(this.flags) === 3);
|
|
|
|
|
|
|
|
mask |= flag_iopl;
|
|
|
|
|
|
|
|
// vip and vif are preserved
|
|
|
|
clear |= flag_vip | flag_vif;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
else
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!this.protected_mode) dbg_assert(this.cpl === 0);
|
|
|
|
|
|
|
|
if(this.cpl)
|
|
|
|
{
|
|
|
|
// cpl > 0
|
|
|
|
// cannot update iopl
|
|
|
|
mask |= flag_iopl;
|
|
|
|
|
|
|
|
if(this.cpl > getiopl(this.flags))
|
|
|
|
{
|
|
|
|
// cpl > iopl
|
|
|
|
// can update interrupt flag but not iopl
|
|
|
|
mask |= flag_interrupt;
|
|
|
|
}
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.flags = (new_flags ^ ((this.flags ^ new_flags) & mask)) & clear | flags_default;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.flags_changed = 0;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.get_stack_pointer = function(mod)
|
|
|
|
{
|
2014-06-28 22:28:11 +02:00
|
|
|
return this.get_seg(reg_ss) + this.stack_reg[this.reg_vsp] + mod | 0;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* returns the "real" instruction pointer,
|
|
|
|
* without segment offset
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.get_real_eip = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.instruction_pointer - this.get_seg(reg_cs) | 0;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.call_interrupt_vector = function(interrupt_nr, is_software_int, error_code)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
dbg_assert(this.instruction_pointer !== undefined);
|
|
|
|
|
2014-06-24 16:10:40 +02:00
|
|
|
if(DEBUG && this.debug.step_mode)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.debug.ops.add(this.instruction_pointer >>> 0);
|
|
|
|
this.debug.ops.add("-- INT " + h(interrupt_nr));
|
|
|
|
this.debug.ops.add(1);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//if(interrupt_nr == 0x13)
|
|
|
|
//{
|
|
|
|
// dbg_log("INT 13");
|
2014-06-15 22:25:17 +02:00
|
|
|
// dbg_log(this.memory.read8(ch) + "/" + this.memory.read8(dh) + "/" + this.memory.read8(cl) + " |" + this.memory.read8(al));
|
|
|
|
// dbg_log("=> ", h(this.memory.read16(es) * 16 + this.memory.read16(bx)));
|
2013-11-07 21:30:18 +01:00
|
|
|
//}
|
|
|
|
|
|
|
|
//if(interrupt_nr == 0x10)
|
|
|
|
//{
|
2014-06-15 22:25:17 +02:00
|
|
|
// dbg_log("int10 ax=" + h(this.reg16[reg_ax], 4) + " '" + String.fromCharCode(this.reg8[reg_al]) + "'");
|
2014-06-24 16:10:40 +02:00
|
|
|
// this.debug.dump_regs_short();
|
2014-06-15 22:25:17 +02:00
|
|
|
// if(this.reg8[reg_ah] == 0xe) vga.tt_write(this.reg8[reg_al]);
|
2013-11-07 21:30:18 +01:00
|
|
|
//}
|
|
|
|
|
|
|
|
//if(interrupt_nr === 0x13)
|
|
|
|
//{
|
2014-06-24 16:10:40 +02:00
|
|
|
// this.debug.dump_regs_short();
|
2013-11-07 21:30:18 +01:00
|
|
|
//}
|
|
|
|
|
2014-01-02 01:05:49 +01:00
|
|
|
//if(interrupt_nr === 6)
|
|
|
|
//{
|
2014-06-15 22:25:17 +02:00
|
|
|
// this.instruction_pointer += 2;
|
2014-01-02 01:05:49 +01:00
|
|
|
// dbg_log("BUG()", LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
// dbg_log("line=" + this.read_imm16() + " " +
|
|
|
|
// "file=" + this.memory.read_string(this.translate_address_read(this.read_imm32s())), LOG_CPU);
|
|
|
|
// this.instruction_pointer -= 8;
|
2014-06-24 16:10:40 +02:00
|
|
|
// this.debug.dump_regs_short();
|
2014-01-02 01:05:49 +01:00
|
|
|
//}
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
//if(interrupt_nr === 0x80)
|
|
|
|
//{
|
|
|
|
// dbg_log("linux syscall");
|
2014-06-24 16:10:40 +02:00
|
|
|
// this.debug.dump_regs_short();
|
2013-11-07 21:30:18 +01:00
|
|
|
//}
|
|
|
|
|
|
|
|
|
2014-01-02 01:05:49 +01:00
|
|
|
//if(interrupt_nr === 14)
|
|
|
|
//{
|
2014-01-03 22:02:43 +01:00
|
|
|
// dbg_log("int14 error_code=" + error_code +
|
2014-06-15 22:25:17 +02:00
|
|
|
// " cr2=" + h(this.cr2 >>> 0) +
|
|
|
|
// " prev=" + h(this.previous_ip >>> 0) +
|
|
|
|
// " cpl=" + this.cpl, LOG_CPU);
|
2014-01-02 01:05:49 +01:00
|
|
|
//}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-24 16:10:40 +02:00
|
|
|
//if(interrupt_nr === 0x40)
|
|
|
|
//{
|
|
|
|
// dbg_log("kolibri syscall");
|
|
|
|
// this.debug.dump_regs_short();
|
|
|
|
//}
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-22 19:10:35 +02:00
|
|
|
// we have to leave hlt_loop at some point, this is a
|
|
|
|
// good place to do it
|
2014-06-29 22:28:55 +02:00
|
|
|
//this.in_hlt && dbg_log("Leave HLT loop", LOG_CPU);
|
2014-06-22 19:10:35 +02:00
|
|
|
this.in_hlt = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// This function could be called from 1) an INT instruction
|
|
|
|
// 2) an instruction that caused an exception 3) an external interrupt
|
|
|
|
// To handle case 3), previous_ip has to be set correctly
|
|
|
|
if(!is_software_int)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.previous_ip = this.instruction_pointer;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.protected_mode)
|
|
|
|
{
|
2014-06-28 22:59:42 +02:00
|
|
|
if(vm86_mode() && (this.cr4 & CR4_VME))
|
2014-01-03 22:02:43 +01:00
|
|
|
{
|
|
|
|
throw unimpl("VME");
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(vm86_mode() && is_software_int && getiopl(this.flags) < 3)
|
2013-12-09 12:30:24 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_gp(0);
|
2013-12-09 12:30:24 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if((interrupt_nr << 3 | 7) > this.idtr_size)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_log(interrupt_nr, LOG_CPU);
|
2013-12-28 23:21:43 +01:00
|
|
|
dbg_trace(LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var addr = this.idtr_offset + (interrupt_nr << 3) | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert((addr & 0xFFF) < 0xFF8);
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.paging)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
addr = this.translate_address_system_read(addr);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var base = this.memory.read16(addr) | this.memory.read16(addr + 6) << 16,
|
|
|
|
selector = this.memory.read16(addr + 2),
|
|
|
|
type = this.memory.read8(addr + 5),
|
2013-11-07 21:30:18 +01:00
|
|
|
dpl = type >> 5 & 3,
|
|
|
|
is_trap;
|
|
|
|
|
|
|
|
if((type & 128) === 0)
|
|
|
|
{
|
|
|
|
// present bit not set
|
|
|
|
throw unimpl("#NP handler");
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(is_software_int && dpl < this.cpl)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_gp(interrupt_nr << 3 | 2);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type &= 31;
|
|
|
|
|
|
|
|
if(type === 14)
|
|
|
|
{
|
|
|
|
is_trap = false;
|
|
|
|
}
|
|
|
|
else if(type === 15)
|
|
|
|
{
|
|
|
|
is_trap = true;
|
|
|
|
}
|
|
|
|
else if(type === 5)
|
|
|
|
{
|
|
|
|
throw unimpl("call int to task gate");
|
|
|
|
}
|
|
|
|
else if(type === 6)
|
|
|
|
{
|
|
|
|
throw unimpl("16 bit interrupt gate");
|
|
|
|
}
|
|
|
|
else if(type === 7)
|
|
|
|
{
|
|
|
|
throw unimpl("16 bit trap gate");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// invalid type
|
2013-12-28 23:21:43 +01:00
|
|
|
dbg_trace(LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_log("invalid type: " + h(type));
|
|
|
|
dbg_log(h(addr) + " " + h(base) + " " + h(selector));
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var info = this.lookup_segment_selector(selector);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(info.is_null)
|
|
|
|
{
|
|
|
|
dbg_log("is null");
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!info.is_executable || info.dpl > this.cpl)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_log("not exec");
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
if(!info.is_present)
|
|
|
|
{
|
|
|
|
dbg_log("not present");
|
|
|
|
throw unimpl("#NP handler");
|
|
|
|
}
|
2013-12-09 12:30:24 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.load_eflags();
|
|
|
|
var old_flags = this.flags;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!info.dc_bit && info.dpl < this.cpl)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
// inter privilege level interrupt
|
2013-12-09 12:30:24 +01:00
|
|
|
// interrupt from vm86 mode
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
var tss_stack_addr = (info.dpl << 3) + 4;
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(tss_stack_addr + 5 > this.tsr_size)
|
|
|
|
{
|
|
|
|
throw unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
tss_stack_addr = tss_stack_addr + this.tsr_offset | 0;
|
|
|
|
|
|
|
|
if(this.paging)
|
|
|
|
{
|
|
|
|
tss_stack_addr = this.translate_address_system_read(tss_stack_addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
var new_esp = this.memory.read32s(tss_stack_addr),
|
|
|
|
new_ss = this.memory.read16(tss_stack_addr + 4),
|
|
|
|
ss_info = this.lookup_segment_selector(new_ss);
|
|
|
|
|
|
|
|
if(ss_info.is_null)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
throw unimpl("#TS handler");
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
if(ss_info.rpl !== info.dpl)
|
|
|
|
{
|
|
|
|
throw unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
if(ss_info.dpl !== info.dpl || !ss_info.rw_bit)
|
|
|
|
{
|
|
|
|
throw unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
if(!ss_info.is_present)
|
|
|
|
{
|
|
|
|
throw unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
var old_esp = this.reg32s[reg_esp],
|
|
|
|
old_ss = this.sreg[reg_ss];
|
|
|
|
|
|
|
|
if(old_flags & flag_vm)
|
|
|
|
{
|
|
|
|
dbg_log("return from vm86 mode");
|
|
|
|
this.debug.dump_regs_short();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.cpl = info.dpl;
|
|
|
|
//dbg_log("int" + h(interrupt_nr, 2) +" from=" + h(this.instruction_pointer >>> 0, 8)
|
|
|
|
// + " cpl=" + cpl + " old ss:esp=" + h(old_ss, 4) + ":" + h(old_esp >>> 0, 8), LOG_CPU);
|
|
|
|
|
|
|
|
this.cpl_changed();
|
|
|
|
|
|
|
|
dbg_assert(typeof info.size === "boolean");
|
|
|
|
if(this.is_32 !== info.size)
|
|
|
|
{
|
|
|
|
this.update_cs_size(info.size);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.flags &= ~flag_vm & ~flag_rf;
|
|
|
|
|
|
|
|
this.reg32s[reg_esp] = new_esp;
|
|
|
|
this.switch_seg(reg_ss, new_ss);
|
|
|
|
|
|
|
|
if(old_flags & flag_vm)
|
|
|
|
{
|
|
|
|
this.push32(this.sreg[reg_gs]);
|
|
|
|
this.push32(this.sreg[reg_fs]);
|
|
|
|
this.push32(this.sreg[reg_ds]);
|
|
|
|
this.push32(this.sreg[reg_es]);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.push32(old_ss);
|
|
|
|
this.push32(old_esp);
|
|
|
|
}
|
|
|
|
else if(info.dc_bit || info.dpl === this.cpl)
|
|
|
|
{
|
|
|
|
if(this.flags & flag_vm)
|
|
|
|
{
|
|
|
|
this.trigger_gp(selector & ~3);
|
|
|
|
}
|
|
|
|
// intra privilege level interrupt
|
|
|
|
|
|
|
|
//dbg_log("int" + h(interrupt_nr, 2) +" from=" + h(this.instruction_pointer, 8), LOG_CPU);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.push32(old_flags);
|
|
|
|
|
|
|
|
this.push32(this.sreg[reg_cs]);
|
|
|
|
this.push32(this.get_real_eip());
|
|
|
|
//dbg_log("pushed eip to " + h(this.reg32s[reg_esp], 8), LOG_CPU);
|
|
|
|
|
|
|
|
if(old_flags & flag_vm)
|
|
|
|
{
|
|
|
|
this.switch_seg(reg_gs, 0);
|
|
|
|
this.switch_seg(reg_fs, 0);
|
|
|
|
this.switch_seg(reg_ds, 0);
|
|
|
|
this.switch_seg(reg_es, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(error_code !== false)
|
|
|
|
{
|
|
|
|
dbg_assert(typeof error_code == "number");
|
|
|
|
this.push32(error_code);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
this.sreg[reg_cs] = selector;
|
|
|
|
//this.switch_seg(reg_cs);
|
|
|
|
|
|
|
|
dbg_assert(typeof info.size === "boolean");
|
|
|
|
if(this.is_32 !== info.size)
|
|
|
|
{
|
|
|
|
this.update_cs_size(info.size);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.segment_limits[reg_cs] = info.real_limit;
|
|
|
|
this.segment_offsets[reg_cs] = info.base;
|
|
|
|
|
|
|
|
//dbg_log("current esp: " + h(this.reg32s[reg_esp]), LOG_CPU);
|
|
|
|
//dbg_log("call int " + h(interrupt_nr >>> 0, 8) +
|
|
|
|
// " from " + h(this.instruction_pointer >>> 0, 8) +
|
|
|
|
// " to " + h(base >>> 0) +
|
|
|
|
// " if=" + +!!(is_trap && this.flags & flag_interrupt) +
|
|
|
|
// " error_code=" + error_code, LOG_CPU);
|
|
|
|
|
|
|
|
this.instruction_pointer = this.get_seg(reg_cs) + base | 0;
|
|
|
|
|
|
|
|
//dbg_log("int" + h(interrupt_nr) + " trap=" + is_trap + " if=" + +!!(this.flags & flag_interrupt));
|
|
|
|
|
|
|
|
if(!is_trap)
|
|
|
|
{
|
|
|
|
// clear int flag for interrupt gates
|
|
|
|
this.flags &= ~flag_interrupt;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-24 16:10:40 +02:00
|
|
|
setTimeout(function() { this.handle_irqs(); }.bind(this), 0);
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// call 4 byte cs:ip interrupt vector from ivt at cpu.memory 0
|
|
|
|
|
|
|
|
//debug.logop(this.instruction_pointer, "callu " + h(interrupt_nr) + "." + h(this.memory.read8(ah)));
|
|
|
|
//dbg_log("callu " + h(interrupt_nr) + "." +
|
|
|
|
// h(this.memory.read8(ah)) + " at " + h(this.instruction_pointer, 8), LOG_CPU, LOG_CPU);
|
|
|
|
|
|
|
|
// push flags, cs:ip
|
|
|
|
this.load_eflags();
|
|
|
|
this.push16(this.flags);
|
|
|
|
this.push16(this.sreg[reg_cs]);
|
|
|
|
this.push16(this.get_real_eip());
|
|
|
|
|
|
|
|
this.flags = this.flags & ~flag_interrupt;
|
|
|
|
|
|
|
|
this.switch_seg(reg_cs, this.memory.read16((interrupt_nr << 2) + 2));
|
|
|
|
this.instruction_pointer = this.get_seg(reg_cs) + this.memory.read16(interrupt_nr << 2) | 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.last_instr_jump = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
v86.prototype.iret16 = function()
|
|
|
|
{
|
|
|
|
if(!this.protected_mode || (vm86_mode() && getiopl(this.flags) === 3))
|
|
|
|
{
|
|
|
|
var ip = this.pop16();
|
|
|
|
|
|
|
|
this.switch_seg(reg_cs, this.pop16());
|
|
|
|
var new_flags = this.pop16();
|
|
|
|
|
|
|
|
this.instruction_pointer = ip + this.get_seg(reg_cs) | 0;
|
|
|
|
this.update_eflags(new_flags);
|
|
|
|
|
|
|
|
this.handle_irqs();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(vm86_mode())
|
|
|
|
{
|
|
|
|
// vm86 mode, iopl != 3
|
|
|
|
this.trigger_gp(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw unimpl("16 bit iret in protected mode");
|
|
|
|
}
|
|
|
|
|
|
|
|
this.last_instr_jump = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
v86.prototype.iret32 = function()
|
|
|
|
{
|
|
|
|
if(!this.protected_mode || (vm86_mode() && getiopl(this.flags) === 3))
|
|
|
|
{
|
|
|
|
if(vm86_mode()) dbg_log("iret in vm86 mode iopl=3", LOG_CPU);
|
|
|
|
|
|
|
|
var ip = this.pop32s();
|
|
|
|
|
|
|
|
this.switch_seg(reg_cs, this.pop32s() & 0xFFFF);
|
|
|
|
var new_flags = this.pop32s();
|
|
|
|
|
|
|
|
this.instruction_pointer = ip + this.get_seg(reg_cs) | 0;
|
|
|
|
this.update_eflags(new_flags);
|
|
|
|
|
|
|
|
this.handle_irqs();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(vm86_mode())
|
|
|
|
{
|
|
|
|
// vm86 mode, iopl != 3
|
|
|
|
this.trigger_gp(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.flags & flag_nt)
|
|
|
|
{
|
|
|
|
if(DEBUG) throw "unimplemented nt";
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
//dbg_log("pop eip from " + h(this.reg32[reg_esp], 8));
|
|
|
|
this.instruction_pointer = this.pop32s();
|
2014-06-24 16:10:40 +02:00
|
|
|
|
|
|
|
//dbg_log("IRET | from " + h(this.previous_ip >>> 0) + " to " + h(this.instruction_pointer >>> 0));
|
|
|
|
//this.debug.dump_regs_short();
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.sreg[reg_cs] = this.pop32s();
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var new_flags = this.pop32s();
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(new_flags & flag_vm)
|
|
|
|
{
|
|
|
|
if(this.cpl === 0)
|
|
|
|
{
|
|
|
|
// return to virtual 8086 mode
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.update_eflags(new_flags);
|
|
|
|
this.flags |= flag_vm;
|
2014-01-02 01:05:49 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
dbg_log("in vm86 mode now " +
|
|
|
|
" cs:eip=" + h(this.sreg[reg_cs]) + ":" + h(this.instruction_pointer >>> 0) +
|
|
|
|
" iopl=" + getiopl(this.flags), LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.switch_seg(reg_cs, this.sreg[reg_cs]);
|
|
|
|
this.instruction_pointer = this.instruction_pointer + this.get_seg(reg_cs) | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var temp_esp = this.pop32s();
|
|
|
|
var temp_ss = this.pop32s();
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.switch_seg(reg_es, this.pop32s() & 0xFFFF);
|
|
|
|
this.switch_seg(reg_ds, this.pop32s() & 0xFFFF);
|
|
|
|
this.switch_seg(reg_fs, this.pop32s() & 0xFFFF);
|
|
|
|
this.switch_seg(reg_gs, this.pop32s() & 0xFFFF);
|
2013-12-28 23:21:43 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.reg32s[reg_esp] = temp_esp;
|
|
|
|
this.switch_seg(reg_ss, temp_ss & 0xFFFF);
|
2013-12-28 23:21:43 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cpl = 3;
|
|
|
|
this.update_cs_size(false);
|
2013-12-09 12:30:24 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.debug.dump_regs_short();
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
return;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2013-12-28 23:21:43 +01:00
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
// ignored if not cpl=0
|
|
|
|
new_flags &= ~flag_vm;
|
2013-12-28 23:21:43 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// protected mode return
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var info = this.lookup_segment_selector(this.sreg[reg_cs]);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(info.is_null)
|
|
|
|
{
|
|
|
|
throw unimpl("is null");
|
|
|
|
}
|
|
|
|
if(!info.is_present)
|
|
|
|
{
|
|
|
|
throw unimpl("not present");
|
|
|
|
}
|
|
|
|
if(!info.is_executable)
|
|
|
|
{
|
|
|
|
throw unimpl("not exec");
|
|
|
|
}
|
|
|
|
if(info.rpl < this.cpl)
|
|
|
|
{
|
|
|
|
throw unimpl("rpl < cpl");
|
|
|
|
}
|
|
|
|
if(info.dc_bit && info.dpl > info.rpl)
|
|
|
|
{
|
|
|
|
throw unimpl("conforming and dpl > rpl");
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(info.rpl > this.cpl)
|
|
|
|
{
|
|
|
|
// outer privilege return
|
|
|
|
var temp_esp = this.pop32s();
|
|
|
|
var temp_ss = this.pop32s();
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2013-12-09 12:30:24 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.reg32s[reg_esp] = temp_esp;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.update_eflags(new_flags);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cpl = info.rpl;
|
|
|
|
this.switch_seg(reg_ss, temp_ss & 0xFFFF);
|
|
|
|
|
|
|
|
//dbg_log("iret cpu.cpl=" + this.cpl + " to " + h(this.instruction_pointer) +
|
|
|
|
// " cs:eip=" + h(this.sreg[reg_cs],4) + ":" + h(this.get_real_eip(), 8) +
|
|
|
|
// " ss:esp=" + h(temp_ss & 0xFFFF, 2) + ":" + h(temp_esp, 8), LOG_CPU);
|
|
|
|
|
|
|
|
this.cpl_changed();
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.update_eflags(new_flags);
|
|
|
|
// same privilege return
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
//dbg_log(h(new_flags) + " " + h(this.flags));
|
|
|
|
//dbg_log("iret to " + h(this.instruction_pointer));
|
|
|
|
}
|
|
|
|
|
|
|
|
dbg_assert(typeof info.size === "boolean");
|
|
|
|
if(info.size !== this.is_32)
|
|
|
|
{
|
|
|
|
this.update_cs_size(info.size);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.segment_limits[reg_cs] = info.real_limit;
|
|
|
|
this.segment_offsets[reg_cs] = info.base;
|
|
|
|
|
|
|
|
this.instruction_pointer = this.instruction_pointer + this.get_seg(reg_cs) | 0;
|
|
|
|
|
|
|
|
|
|
|
|
//dbg_log("iret if=" + (this.flags & flag_interrupt) + " cpl=" + this.cpl + " eip=" + h(this.instruction_pointer >>> 0, 8), LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.handle_irqs();
|
|
|
|
this.last_instr_jump = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
v86.prototype.hlt_op = function()
|
|
|
|
{
|
|
|
|
dbg_assert(this.instruction_pointer !== undefined);
|
|
|
|
|
|
|
|
if(this.cpl)
|
|
|
|
{
|
|
|
|
this.trigger_gp(0);
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// hlt
|
|
|
|
if((this.flags & flag_interrupt) === 0)
|
|
|
|
{
|
|
|
|
this.debug.show("cpu halted");
|
|
|
|
this.stopped = true;
|
|
|
|
if(DEBUG) this.debug.dump_regs();
|
|
|
|
throw "HALT";
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
else
|
|
|
|
{
|
2014-06-22 19:10:35 +02:00
|
|
|
// get out of here and into hlt_loop
|
2014-06-15 22:25:17 +02:00
|
|
|
this.in_hlt = true;
|
2014-06-22 19:10:35 +02:00
|
|
|
throw MAGIC_CPU_EXCEPTION;
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// assumes ip to point to the byte before the next instruction
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.raise_exception = function(interrupt_nr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
if(DEBUG)
|
|
|
|
{
|
|
|
|
// warn about error
|
|
|
|
dbg_log("Exception " + h(interrupt_nr), LOG_CPU);
|
2013-12-28 23:21:43 +01:00
|
|
|
dbg_trace(LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.debug.dump_regs_short();
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.call_interrupt_vector(interrupt_nr, false, false);
|
2014-05-06 20:24:46 +02:00
|
|
|
throw MAGIC_CPU_EXCEPTION;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.raise_exception_with_code = function(interrupt_nr, error_code)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
if(DEBUG)
|
|
|
|
{
|
|
|
|
dbg_log("Exception " + h(interrupt_nr) + " err=" + h(error_code), LOG_CPU);
|
2013-12-28 23:21:43 +01:00
|
|
|
dbg_trace(LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.debug.dump_regs_short();
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.call_interrupt_vector(interrupt_nr, false, error_code);
|
2014-05-06 20:24:46 +02:00
|
|
|
throw MAGIC_CPU_EXCEPTION;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.trigger_de = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.raise_exception(0);
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.trigger_ud = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.raise_exception(6);
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.trigger_nm = function()
|
2014-01-03 22:02:43 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.raise_exception(7);
|
|
|
|
};
|
2014-01-03 22:02:43 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.trigger_gp = function(code)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.raise_exception_with_code(13, code);
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.trigger_np = function(code)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.raise_exception_with_code(11, code);
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.trigger_ss = function(code)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.raise_exception_with_code(12, code);
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {number} seg
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.seg_prefix = function(seg)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
dbg_assert(this.segment_prefix === SEG_PREFIX_NONE);
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert(seg >= 0 && seg <= 5);
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.segment_prefix = seg;
|
|
|
|
this.table[this.read_imm8()](this);
|
|
|
|
this.segment_prefix = SEG_PREFIX_NONE;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get segment base by prefix or default
|
|
|
|
* @param {number} default_segment
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.get_seg_prefix = function(default_segment /*, offset*/)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.segment_prefix === SEG_PREFIX_NONE)
|
|
|
|
{
|
|
|
|
return this.get_seg(default_segment /*, offset*/);
|
|
|
|
}
|
|
|
|
else if(this.segment_prefix === SEG_PREFIX_ZERO)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.get_seg(this.segment_prefix /*, offset*/);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get segment base
|
|
|
|
* @param {number} segment
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.get_seg = function(segment /*, offset*/)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_assert(segment >= 0 && segment < 8);
|
2014-06-15 22:25:17 +02:00
|
|
|
dbg_assert(this.protected_mode || (this.sreg[segment] << 4) == this.segment_offsets[segment]);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.protected_mode)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.segment_is_null[segment])
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
// trying to access null segment
|
|
|
|
if(DEBUG)
|
|
|
|
{
|
|
|
|
dbg_log("Load null segment: " + h(segment), LOG_CPU);
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
// - validate segment limits
|
|
|
|
// - validate if segment is writable
|
|
|
|
// - set accessed bit
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.segment_offsets[segment];
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.handle_irqs = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-24 16:10:40 +02:00
|
|
|
if(DEBUG && this.debug.step_mode)
|
|
|
|
return;
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.devices.pic)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
dbg_assert(!this.page_fault);
|
2013-12-28 23:21:43 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if((this.flags & flag_interrupt) && !this.page_fault)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.devices.pic.check_irqs();
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.test_privileges_for_io = function(port, size)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.protected_mode && (this.cpl > getiopl(this.flags) || (this.flags & flag_vm)))
|
2013-11-25 19:57:46 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.tsr_size >= 0x67)
|
2013-12-09 12:30:24 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
var iomap_base = this.memory.read16(this.translate_address_system_read(this.tsr_offset + 0x64 + 2)),
|
2013-12-09 12:30:24 +01:00
|
|
|
high_port = port + size - 1;
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.tsr_size >= iomap_base + (high_port >> 3))
|
2013-12-09 12:30:24 +01:00
|
|
|
{
|
|
|
|
var mask = ((1 << size) - 1) << (port & 7),
|
2014-06-15 22:25:17 +02:00
|
|
|
addr = this.translate_address_system_read(this.tsr_offset + iomap_base + (port >> 3)),
|
2013-12-09 12:30:24 +01:00
|
|
|
port_info = (mask & 0xFF00) ?
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.read16(addr) : this.memory.read8(addr);
|
2013-12-09 12:30:24 +01:00
|
|
|
|
|
|
|
if(!(port_info & mask))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dbg_log("#GP for port io port=" + h(port) + " size=" + size, LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_gp(0);
|
2013-11-25 19:57:46 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.cpuid = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
// cpuid
|
|
|
|
// TODO: Fill in with less bogus values
|
|
|
|
|
|
|
|
// http://lxr.linux.no/linux+%2a/arch/x86/include/asm/cpufeature.h
|
2014-01-08 03:22:30 +01:00
|
|
|
// http://www.sandpile.org/x86/cpuid.htm
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var id = this.reg32s[reg_eax],
|
|
|
|
extended = this.reg32s[reg_eax] >>> 31;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if((id & 0x7FFFFFFF) === 0)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.reg32s[reg_eax] = 2;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(id === 0)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.reg32s[reg_ebx] = 0x756E6547|0; // Genu
|
|
|
|
this.reg32s[reg_edx] = 0x49656E69|0; // ineI
|
|
|
|
this.reg32s[reg_ecx] = 0x6C65746E|0; // ntel
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(id === 1)
|
|
|
|
{
|
|
|
|
// pentium
|
2014-06-15 22:25:17 +02:00
|
|
|
this.reg32s[reg_eax] = 3 | 6 << 4 | 15 << 8;
|
|
|
|
this.reg32s[reg_ebx] = 0;
|
|
|
|
this.reg32s[reg_ecx] = 0;
|
|
|
|
this.reg32s[reg_edx] = this.fpu !== undefined | 1 << 1 | 1 << 3 | 1 << 4 | 1 << 5 |
|
2014-01-08 03:22:30 +01:00
|
|
|
1 << 8 | 1 << 13 | 1 << 15;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else if(id === 2)
|
|
|
|
{
|
|
|
|
// Taken from http://siyobik.info.gf/main/reference/instruction/CPUID
|
2014-06-15 22:25:17 +02:00
|
|
|
this.reg32s[reg_eax] = 0x665B5001|0;
|
|
|
|
this.reg32s[reg_ebx] = 0;
|
|
|
|
this.reg32s[reg_ecx] = 0;
|
|
|
|
this.reg32s[reg_edx] = 0x007A7000;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else if(id === (0x80860000 | 0))
|
|
|
|
{
|
2014-01-08 03:22:30 +01:00
|
|
|
// Transmeta level
|
2014-06-15 22:25:17 +02:00
|
|
|
this.reg32s[reg_eax] = 0;
|
|
|
|
this.reg32s[reg_ebx] = 0;
|
|
|
|
this.reg32s[reg_ecx] = 0;
|
|
|
|
this.reg32s[reg_edx] = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-01-08 03:22:30 +01:00
|
|
|
else if((id & (0xC0000000|0)) === 0x40000000)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-01-08 03:22:30 +01:00
|
|
|
// Not sure
|
2014-06-15 22:25:17 +02:00
|
|
|
this.reg32s[reg_eax] = 0;
|
|
|
|
this.reg32s[reg_ebx] = 0;
|
|
|
|
this.reg32s[reg_ecx] = 0;
|
|
|
|
this.reg32s[reg_edx] = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-01-08 03:22:30 +01:00
|
|
|
if(DEBUG) throw "cpuid: unimplemented eax: " + h(id);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.update_cs_size = function(new_size)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.is_32 = this.operand_size_32 = this.address_size_32 = new_size;
|
2014-01-03 22:02:43 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.update_operand_size();
|
|
|
|
this.update_address_size();
|
2014-01-03 22:02:43 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.translator !== undefined)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.translator.clear_cache();
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.update_operand_size = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.operand_size_32)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.table = this.table32;
|
|
|
|
this.table0F = this.table0F_32;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.table = this.table16;
|
|
|
|
this.table0F = this.table0F_16;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.update_address_size = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.address_size_32)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
//this.modrm_resolve = this.modrm_resolve32;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.regv = this.reg32s;
|
|
|
|
this.reg_vcx = reg_ecx;
|
|
|
|
this.reg_vsi = reg_esi;
|
|
|
|
this.reg_vdi = reg_edi;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
//this.modrm_resolve = this.modrm_resolve16;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.regv = this.reg16;
|
|
|
|
this.reg_vcx = reg_cx;
|
|
|
|
this.reg_vsi = reg_si;
|
|
|
|
this.reg_vdi = reg_di;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {number} selector
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.lookup_segment_selector = function(selector)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
var is_gdt = (selector & 4) === 0,
|
|
|
|
selector_offset = selector & ~7,
|
|
|
|
info,
|
|
|
|
table_offset,
|
|
|
|
table_limit;
|
|
|
|
|
|
|
|
info = {
|
|
|
|
rpl: selector & 3,
|
|
|
|
from_gdt: is_gdt,
|
|
|
|
is_null: false,
|
|
|
|
is_valid: true,
|
2014-01-08 03:22:30 +01:00
|
|
|
|
|
|
|
base: 0,
|
|
|
|
access: 0,
|
|
|
|
flags: 0,
|
|
|
|
limit: 0,
|
|
|
|
type: 0,
|
|
|
|
dpl: 0,
|
|
|
|
is_system: false,
|
|
|
|
is_present: false,
|
|
|
|
is_executable: false,
|
|
|
|
rw_bit: false,
|
|
|
|
dc_bit: false,
|
|
|
|
size: false,
|
|
|
|
granularity: false,
|
|
|
|
real_limit: false,
|
|
|
|
is_writable: false,
|
|
|
|
is_readable: false,
|
2014-02-11 21:42:28 +01:00
|
|
|
table_offset: 0,
|
2013-11-07 21:30:18 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
if(is_gdt)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
table_offset = this.gdtr_offset;
|
|
|
|
table_limit = this.gdtr_size;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
table_offset = this.ldtr_offset
|
|
|
|
table_limit = this.ldtr_size;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(selector_offset === 0)
|
|
|
|
{
|
|
|
|
info.is_null = true;
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
// limit is the number of entries in the table minus one
|
|
|
|
if((selector_offset >> 3) > table_limit)
|
|
|
|
{
|
|
|
|
info.is_valid = false;
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
table_offset = table_offset + selector_offset | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.paging)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
table_offset = this.translate_address_system_read(table_offset);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-02-11 21:42:28 +01:00
|
|
|
info.table_offset = table_offset;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
info.base = this.memory.read16(table_offset + 2) | this.memory.read8(table_offset + 4) << 16 |
|
|
|
|
this.memory.read8(table_offset + 7) << 24,
|
|
|
|
info.access = this.memory.read8(table_offset + 5),
|
|
|
|
info.flags = this.memory.read8(table_offset + 6) >> 4,
|
|
|
|
info.limit = this.memory.read16(table_offset) | (this.memory.read8(table_offset + 6) & 0xF) << 16,
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// used if system
|
|
|
|
info.type = info.access & 0xF;
|
|
|
|
|
|
|
|
info.dpl = info.access >> 5 & 3;
|
|
|
|
|
|
|
|
info.is_system = (info.access & 0x10) === 0;
|
|
|
|
info.is_present = (info.access & 0x80) === 0x80;
|
|
|
|
info.is_executable = (info.access & 8) === 8;
|
|
|
|
|
|
|
|
info.rw_bit = (info.access & 2) === 2;
|
|
|
|
info.dc_bit = (info.access & 4) === 4;
|
|
|
|
|
|
|
|
info.size = (info.flags & 4) === 4;
|
|
|
|
info.granularity = (info.flags & 8) === 8;
|
|
|
|
|
|
|
|
|
|
|
|
if(info.gr_bit)
|
|
|
|
{
|
|
|
|
info.real_limit = (info.limit << 12 | 0xFFF) >>> 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
info.real_limit = info.limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
info.is_writable = info.rw_bit && !info.is_executable;
|
|
|
|
info.is_readable = info.rw_bit || !info.is_executable;
|
|
|
|
|
|
|
|
return info;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {number} reg
|
|
|
|
* @param {number} selector
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.switch_seg = function(reg, selector)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_assert(reg >= 0 && reg <= 5);
|
|
|
|
dbg_assert(typeof selector === "number" && selector < 0x10000 && selector >= 0);
|
|
|
|
|
|
|
|
if(reg === reg_cs)
|
|
|
|
{
|
2014-06-28 22:59:42 +02:00
|
|
|
this.protected_mode = (this.cr0 & CR0_PE) === CR0_PE;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!this.protected_mode || vm86_mode())
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.sreg[reg] = selector;
|
|
|
|
this.segment_is_null[reg] = 0;
|
|
|
|
this.segment_limits[reg] = 0xFFFFF;
|
|
|
|
this.segment_offsets[reg] = selector << 4;
|
2013-11-07 21:30:18 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var info = this.lookup_segment_selector(selector);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(reg === reg_ss)
|
|
|
|
{
|
|
|
|
if(info.is_null)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_gp(0);
|
2013-11-07 21:30:18 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(!info.is_valid ||
|
|
|
|
info.is_system ||
|
2014-06-15 22:25:17 +02:00
|
|
|
info.rpl !== this.cpl ||
|
2013-11-07 21:30:18 +01:00
|
|
|
!info.is_writable ||
|
2014-06-15 22:25:17 +02:00
|
|
|
info.dpl !== this.cpl)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_gp(selector & ~3);
|
2013-11-07 21:30:18 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(!info.is_present)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_ss(selector & ~3);
|
2013-11-07 21:30:18 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.stack_size_32 = info.size;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(info.size)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.stack_reg = this.reg32s;
|
|
|
|
this.reg_vsp = reg_esp;
|
|
|
|
this.reg_vbp = reg_ebp;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.stack_reg = this.reg16;
|
|
|
|
this.reg_vsp = reg_sp;
|
|
|
|
this.reg_vbp = reg_bp;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(reg === reg_cs)
|
|
|
|
{
|
|
|
|
if(!info.is_executable)
|
|
|
|
{
|
|
|
|
// cs not executable
|
|
|
|
dbg_log(info + " " + h(selector & ~3), LOG_CPU);
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.is_system)
|
|
|
|
{
|
|
|
|
dbg_log(info + " " + h(selector & ~3), LOG_CPU);
|
|
|
|
throw unimpl("load system segment descriptor, type = " + (info.access & 15));
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.dc_bit && (info.dpl !== info.rpl))
|
|
|
|
{
|
|
|
|
dbg_log(info + " " + h(selector & ~3), LOG_CPU);
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(info.rpl !== this.cpl)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_log(info + " " + h(selector & ~3), LOG_CPU);
|
|
|
|
throw unimpl("privilege change");
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
dbg_assert(this.cpl === info.dpl);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!info.dc_bit && info.dpl < this.cpl)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2013-12-20 22:04:58 +01:00
|
|
|
throw unimpl("inter privilege call");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(info.dc_bit || info.dpl === this.cpl)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
// ok
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// PE = 1, interrupt or trap gate, nonconforming code segment, DPL > CPL
|
|
|
|
dbg_log(info + " " + h(selector & ~3), LOG_CPU);
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
dbg_assert(typeof info.size === "boolean");
|
|
|
|
if(info.size !== this.is_32)
|
2014-05-12 18:44:28 +02:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.update_cs_size(info.size);
|
2014-05-12 18:44:28 +02:00
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// es, ds, fs, gs
|
|
|
|
if(info.is_null)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.sreg[reg] = selector;
|
|
|
|
this.segment_is_null[reg] = 1;
|
2013-11-07 21:30:18 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if(!info.is_valid ||
|
|
|
|
info.is_system ||
|
|
|
|
!info.is_readable ||
|
|
|
|
((!info.is_executable || !info.dc_bit) &&
|
|
|
|
info.rpl > info.dpl &&
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cpl > info.dpl))
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_gp(selector & ~3);
|
2013-11-07 21:30:18 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(!info.is_present)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_np(selector & ~3);
|
2013-11-07 21:30:18 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//dbg_log("seg " + reg + " " + h(info.base));
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.segment_is_null[reg] = 0;
|
|
|
|
this.segment_limits[reg] = info.real_limit;
|
|
|
|
this.segment_infos[reg] = 0; // TODO
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.segment_offsets[reg] = info.base;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.sreg[reg] = selector;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
return true;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.load_tr = function(selector)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
var info = this.lookup_segment_selector(selector);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
//dbg_log("load tr");
|
|
|
|
|
|
|
|
if(!info.from_gdt)
|
|
|
|
{
|
|
|
|
throw unimpl("TR can only be loaded from GDT");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.is_null)
|
|
|
|
{
|
|
|
|
dbg_log("#GP(0) | tried to load null selector (ltr)");
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_present)
|
|
|
|
{
|
|
|
|
dbg_log("#GP | present bit not set (ltr)");
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_system)
|
|
|
|
{
|
|
|
|
dbg_log("#GP | ltr: not a system entry");
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.type !== 9)
|
|
|
|
{
|
|
|
|
dbg_log("#GP | ltr: invalid type (type = " + info.type + ")");
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tsr_size = info.limit;
|
|
|
|
this.tsr_offset = info.base;
|
|
|
|
this.tsr_selector = selector;
|
2014-02-11 21:42:28 +01:00
|
|
|
|
|
|
|
// mark task as busy
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.write8(info.table_offset + 5, this.memory.read8(info.table_offset + 5) | 2);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
//dbg_log("tsr at " + h(tsr_offset) + "; (" + this.tsr_size + " bytes)");
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.load_ldt = function(selector)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
var info = this.lookup_segment_selector(selector);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(info.is_null)
|
|
|
|
{
|
|
|
|
// invalid
|
2014-06-15 22:25:17 +02:00
|
|
|
this.ldtr_size = 0;
|
|
|
|
this.ldtr_offset = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.from_gdt)
|
|
|
|
{
|
|
|
|
throw unimpl("LDTR can only be loaded from GDT");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_present)
|
|
|
|
{
|
|
|
|
dbg_log("lldt: present bit not set");
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_system)
|
|
|
|
{
|
|
|
|
dbg_log("lldt: not a system entry");
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.type !== 2)
|
|
|
|
{
|
|
|
|
dbg_log("lldt: invalid type (" + info.type + ")");
|
|
|
|
throw unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.ldtr_size = info.limit;
|
|
|
|
this.ldtr_offset = info.base;
|
|
|
|
this.ldtr_selector = selector;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
//dbg_log("ldt at " + h(ldtr_offset) + "; (" + this.ldtr_size + " bytes)");
|
|
|
|
};
|
|
|
|
|
|
|
|
v86.prototype.arpl = function(seg, r16)
|
|
|
|
{
|
|
|
|
this.flags_changed &= ~flag_zero;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if((seg & 3) < (this.reg16[r16] & 3))
|
|
|
|
{
|
|
|
|
this.flags |= flag_zero;
|
|
|
|
return seg & ~3 | this.reg16[r16] & 3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.flags &= ~flag_zero;
|
|
|
|
return seg;
|
|
|
|
}
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.clear_tlb = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
// clear tlb excluding global pages
|
2014-06-15 22:25:17 +02:00
|
|
|
this.last_virt_eip = -1;
|
|
|
|
this.last_virt_esp = -1;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_info.set(this.tlb_info_global);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
//dbg_log("page table loaded", LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.full_clear_tlb = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
// clear tlb including global pages
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_info_global = new Uint8Array(1 << 20);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.clear_tlb();
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.invlpg = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
var page = addr >>> 12;
|
2013-12-01 23:36:37 +01:00
|
|
|
//dbg_log("invlpg: " + h(page), LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_info[page] = 0;
|
|
|
|
this.tlb_info_global[page] = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.last_virt_eip = -1;
|
|
|
|
this.last_virt_esp = -1;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.translate_address_read = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!this.paging)
|
|
|
|
{
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.cpl)
|
|
|
|
{
|
|
|
|
return this.translate_address_user_read(addr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return this.translate_address_system_read(addr);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
v86.prototype.translate_address_write = function(addr)
|
|
|
|
{
|
|
|
|
if(!this.paging)
|
|
|
|
{
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.cpl)
|
|
|
|
{
|
|
|
|
return this.translate_address_user_write(addr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return this.translate_address_system_write(addr);
|
|
|
|
}
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.translate_address_user_write = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
var base = addr >>> 12;
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.tlb_info[base] & TLB_USER_WRITE)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.tlb_user_write[base] ^ addr;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.do_page_translation(addr, 1, 1) | addr & 0xFFF;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.translate_address_user_read = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
var base = addr >>> 12;
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.tlb_info[base] & TLB_USER_READ)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.tlb_user_read[base] ^ addr;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.do_page_translation(addr, 0, 1) | addr & 0xFFF;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.translate_address_system_write = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
var base = addr >>> 12;
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.tlb_info[base] & TLB_SYSTEM_WRITE)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.tlb_system_write[base] ^ addr;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.do_page_translation(addr, 1, 0) | addr & 0xFFF;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.translate_address_system_read = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
var base = addr >>> 12;
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.tlb_info[base] & TLB_SYSTEM_READ)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.tlb_system_read[base] ^ addr;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.do_page_translation(addr, 0, 0) | addr & 0xFFF;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {number}
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.do_page_translation = function(addr, for_writing, user)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
var page = addr >>> 12,
|
2014-06-15 22:25:17 +02:00
|
|
|
page_dir_addr = (this.cr3 >>> 2) + (page >> 10),
|
|
|
|
page_dir_entry = this.memory.mem32s[page_dir_addr],
|
2013-11-07 21:30:18 +01:00
|
|
|
high,
|
|
|
|
can_write = true,
|
|
|
|
global,
|
|
|
|
cachable = true,
|
|
|
|
allow_user = true;
|
|
|
|
|
2014-01-05 03:17:40 +01:00
|
|
|
dbg_assert(addr < 0x80000000);
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
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
|
2013-12-01 23:36:37 +01:00
|
|
|
//dbg_log("#PF not present", LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cr2 = addr;
|
|
|
|
this.trigger_pagefault(for_writing, user, 0);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// never reached as this.trigger_pagefault throws up
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if((page_dir_entry & 2) === 0)
|
|
|
|
{
|
|
|
|
can_write = false;
|
|
|
|
|
2014-06-28 23:00:00 +02:00
|
|
|
if(for_writing && (user || (this.cr0 & CR0_WP)))
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cr2 = addr;
|
|
|
|
this.trigger_pagefault(for_writing, user, 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if((page_dir_entry & 4) === 0)
|
|
|
|
{
|
|
|
|
allow_user = false;
|
|
|
|
|
|
|
|
if(user)
|
|
|
|
{
|
|
|
|
// "Page Fault: page table accessed by non-supervisor";
|
2013-12-01 23:36:37 +01:00
|
|
|
//dbg_log("#PF supervisor", LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cr2 = addr;
|
|
|
|
this.trigger_pagefault(for_writing, user, 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if((page_dir_entry & 0x10) === 0)
|
|
|
|
{
|
|
|
|
cachable = false;
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(page_dir_entry & this.page_size_extensions)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
// size bit is set
|
|
|
|
|
|
|
|
// set the accessed and dirty bits
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.mem32s[page_dir_addr] = page_dir_entry | 0x20 | for_writing << 6;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
high = (page_dir_entry & 0xFFC00000) | (page << 12 & 0x3FF000);
|
|
|
|
|
|
|
|
global = page_dir_entry & 0x100;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var page_table_addr = ((page_dir_entry & 0xFFFFF000) >>> 2) + (page & 0x3FF),
|
2014-06-15 22:25:17 +02:00
|
|
|
page_table_entry = this.memory.mem32s[page_table_addr];
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(!(page_table_entry & 1))
|
|
|
|
{
|
2013-12-01 23:36:37 +01:00
|
|
|
//dbg_log("#PF not present table", LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cr2 = addr;
|
|
|
|
this.trigger_pagefault(for_writing, user, 0);
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if((page_table_entry & 2) === 0)
|
|
|
|
{
|
|
|
|
can_write = false;
|
|
|
|
|
2014-06-28 23:00:00 +02:00
|
|
|
if(for_writing && (user || (this.cr0 & CR0_WP)))
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2013-12-01 23:36:37 +01:00
|
|
|
//dbg_log("#PF not writable page", LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cr2 = addr;
|
|
|
|
this.trigger_pagefault(for_writing, user, 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if((page_table_entry & 4) === 0)
|
|
|
|
{
|
|
|
|
allow_user = false;
|
|
|
|
|
|
|
|
if(user)
|
|
|
|
{
|
2013-12-01 23:36:37 +01:00
|
|
|
//dbg_log("#PF not supervisor page", LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cr2 = addr;
|
|
|
|
this.trigger_pagefault(for_writing, user, 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if((page_table_entry & 0x10) === 0)
|
|
|
|
{
|
|
|
|
cachable = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the accessed and dirty bits
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory.mem32s[page_dir_addr] = page_dir_entry | 0x20;
|
|
|
|
this.memory.mem32s[page_table_addr] = page_table_entry | 0x20 | for_writing << 6;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
high = page_table_entry & 0xFFFFF000;
|
|
|
|
|
|
|
|
global = page_table_entry & 0x100;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(cachable)
|
|
|
|
{
|
|
|
|
var cache_entry = high ^ page << 12,
|
|
|
|
info = 0;
|
|
|
|
|
|
|
|
if(allow_user)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_user_read[page] = cache_entry;
|
2013-11-07 21:30:18 +01:00
|
|
|
info |= TLB_USER_READ;
|
|
|
|
|
|
|
|
if(can_write)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_user_write[page] = cache_entry;
|
2013-11-07 21:30:18 +01:00
|
|
|
info |= TLB_USER_WRITE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_system_read[page] = cache_entry;
|
2013-11-07 21:30:18 +01:00
|
|
|
info |= TLB_SYSTEM_READ;
|
|
|
|
|
|
|
|
if(can_write)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_system_write[page] = cache_entry;
|
2013-11-07 21:30:18 +01:00
|
|
|
info |= TLB_SYSTEM_WRITE;
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_info[page] |= info;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(global)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_info_global[page] = info;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-22 22:15:41 +01:00
|
|
|
return high;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2014-02-22 22:15:41 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.writable_or_pagefault = function(addr, size)
|
2014-02-22 22:15:41 +01:00
|
|
|
{
|
|
|
|
dbg_assert(size < 0x1000, "not supported yet");
|
|
|
|
dbg_assert(size > 0);
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!this.paging)
|
2014-02-22 22:15:41 +01:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var user = this.cpl ? 1 : 0,
|
|
|
|
mask = this.cpl ? TLB_USER_WRITE : TLB_SYSTEM_WRITE,
|
2014-02-22 22:15:41 +01:00
|
|
|
base = addr >>> 12;
|
|
|
|
|
|
|
|
if((addr & 0xFFF) + size - 1 >= 0x1000)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if((this.tlb_info[base + 1] & mask) === 0)
|
2014-02-22 22:15:41 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.do_page_translation(addr + size - 1, 1, user);
|
2014-02-22 22:15:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if((this.tlb_info[base] & mask) === 0)
|
2014-02-22 22:15:41 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.do_page_translation(addr, 1, user);
|
2014-02-22 22:15:41 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
v86.prototype.trigger_pagefault = function(write, user, present)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-29 22:28:55 +02:00
|
|
|
//dbg_log("page fault w=" + write + " u=" + user + " p=" + present +
|
|
|
|
// " eip=" + h(this.previous_ip >>> 0, 8) +
|
|
|
|
// " cr2=" + h(this.cr2 >>> 0, 8), LOG_CPU);
|
|
|
|
//dbg_trace(LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2013-12-28 23:21:43 +01:00
|
|
|
// likely invalid pointer reference
|
2014-06-15 22:25:17 +02:00
|
|
|
//if((this.cr2 >>> 0) < 0x100)
|
2013-12-28 23:21:43 +01:00
|
|
|
//{
|
|
|
|
// throw "stop";
|
|
|
|
//}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.page_fault)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2013-12-28 23:21:43 +01:00
|
|
|
dbg_trace(LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
throw unimpl("Double fault");
|
|
|
|
}
|
|
|
|
|
2014-01-02 01:05:49 +01:00
|
|
|
// invalidate tlb entry
|
2014-06-15 22:25:17 +02:00
|
|
|
var page = this.cr2 >>> 12;
|
2014-01-02 01:05:49 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_info[page] = 0;
|
|
|
|
this.tlb_info_global[page] = 0;
|
2014-01-02 01:05:49 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.page_fault = true;
|
|
|
|
this.call_interrupt_vector(14, false, user << 2 | write << 1 | present);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-05-06 20:24:46 +02:00
|
|
|
throw MAGIC_CPU_EXCEPTION;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|