Wired up CR0 and CR3 to the new Bus paging functions
This commit is contained in:
parent
5d2a7b13a3
commit
e057bd7a05
6 changed files with 149 additions and 45 deletions
|
|
@ -660,17 +660,17 @@ Bus.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
|
||||||
* Whenever the CPU turns paging off, disablePageBlocks() must be called to restore the original physical
|
* Whenever the CPU turns paging off, disablePageBlocks() must be called to restore the original physical
|
||||||
* memory mapping.
|
* memory mapping.
|
||||||
*
|
*
|
||||||
* This also requires that PAGEBLOCKS be true, ensuring that the Bus is preconfigured with 4Kb memory
|
* This also requires that PAGEBLOCKS be enabled, to ensure that the Bus is preconfigured with 4Kb memory
|
||||||
* mapping granularity.
|
* mapping granularity.
|
||||||
*
|
*
|
||||||
* The first time this function is called, aMemBlocks is stashed in aPhysBlocks, and aMemBlocks is then
|
* The first time this function is called, aMemBlocks is stashed in aPhysBlocks, and aMemBlocks is then
|
||||||
* reinitialized with special "unpaged" Memory blocks that know how to perform page directory/page table
|
* reinitialized with special "unpaged" Memory blocks that know how to perform page directory/page table
|
||||||
* lookup and replace themselves with special "paged" Memory blocks that reference memory from the
|
* lookup and replace themselves with special "paged" Memory blocks that reference memory from the
|
||||||
* appropriate block in aPhysBlocks. A parallel array, aMemPaged, keeps track of which blocks have been
|
* appropriate block in aPhysBlocks. A parallel array, aPageBlockNums, keeps track of which block numbers
|
||||||
* "paged", so that whenever CR3 is updated, just those blocks can be "unpaged" again.
|
* have been "paged", so that whenever CR3 is updated, just those blocks can be "unpaged" again.
|
||||||
*
|
*
|
||||||
* @this {Bus}
|
* @this {Bus}
|
||||||
* @param {number} addrPD is the starting physical address of the CPU's page directory
|
* @param {number} addrPD is the starting physical address of the CPU's page directory (ie, from regCR3)
|
||||||
*/
|
*/
|
||||||
Bus.prototype.enablePageBlocks = function(addrPD)
|
Bus.prototype.enablePageBlocks = function(addrPD)
|
||||||
{
|
{
|
||||||
|
|
@ -681,18 +681,44 @@ Bus.prototype.enablePageBlocks = function(addrPD)
|
||||||
this.addrPD = addrPD;
|
this.addrPD = addrPD;
|
||||||
if (!this.aPhysBlocks) {
|
if (!this.aPhysBlocks) {
|
||||||
this.aPhysBlocks = this.aMemBlocks;
|
this.aPhysBlocks = this.aMemBlocks;
|
||||||
var block = new Memory(null, 0, 0, Memory.TYPE.UNPAGED, null, this);
|
this.blockUnpaged = new Memory(null, 0, 0, Memory.TYPE.UNPAGED, null, this);
|
||||||
this.aMemBlocks = new Array(this.blockTotal);
|
this.aMemBlocks = new Array(this.blockTotal);
|
||||||
for (var iBlock = 0; iBlock < this.blockTotal; iBlock++) {
|
for (var iBlock = 0; iBlock < this.blockTotal; iBlock++) {
|
||||||
this.aMemBlocks[iBlock] = block;
|
this.aMemBlocks[iBlock] = this.blockUnpaged;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < this.aPageBlockNums.length; i++) {
|
||||||
|
this.aMemBlocks[this.aPageBlockNums[i]] = this.blockUnpaged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.aPageBlockNums = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mapPageBlock(addr, fWrite)
|
* mapPageBlock(addr, fWrite)
|
||||||
*
|
*
|
||||||
* Locate the corresponding physical PDE, PTE and memory blocks for the given linear address.
|
* Locate the corresponding physical PDE, PTE and memory blocks for the given linear address, and then
|
||||||
|
* upgrade the block from an "unpaged" Memory block to a new "paged" Memory block; all future accesses to
|
||||||
|
* the current page will go directly to that block, instead of coming here through the "unpaged" block
|
||||||
|
* handlers.
|
||||||
|
*
|
||||||
|
* Note that since the incoming address (addr) is a linear address, we never need to mask it with busMask,
|
||||||
|
* but all the intermediate (PDE, PTE) and final physical addresses we calculate should still be masked.
|
||||||
|
*
|
||||||
|
* Granted, busMask on a 32-bit bus is generally going to be 0xffffffff (-1), so making might seem like
|
||||||
|
* a waste of time; however, if we decide to once again rely on busMask for emulating A20 wrap-around
|
||||||
|
* (instead of changing the physical memory map to alias the 2nd Mb to the 1st Mb), then performing
|
||||||
|
* consistent masking will be important.
|
||||||
|
*
|
||||||
|
* Also, addrPDE, addrPTE and addrPhys do not need any offsets added to them, because we immediately shift
|
||||||
|
* the offset portion of those addresses out (see TODOs below). But for now, at least for debugging and
|
||||||
|
* documentation purposes, my preference is to perform full address calculations.
|
||||||
|
*
|
||||||
|
* Besides, this should not be a performance-critical function; it's normally called only once per "unpaged"
|
||||||
|
* page. Obviously, if CR3 is constantly being updated, that will trigger repeated calls to enablePageBlocks(),
|
||||||
|
* which will perform our equivalent of a TLB flush (ie, resetting all "paged" blocks back to "unpaged" blocks).
|
||||||
|
* That would hurt our performance, but it would hurt performance on a real machine as well, so let's see
|
||||||
|
* what real-world scenarios we run into.
|
||||||
*
|
*
|
||||||
* @this {Bus}
|
* @this {Bus}
|
||||||
* @param {number} addr is a linear address
|
* @param {number} addr is a linear address
|
||||||
|
|
@ -702,7 +728,7 @@ Bus.prototype.enablePageBlocks = function(addrPD)
|
||||||
Bus.prototype.mapPageBlock = function(addr, fWrite)
|
Bus.prototype.mapPageBlock = function(addr, fWrite)
|
||||||
{
|
{
|
||||||
var offPDE = (addr & X86.LADDR.PDE.MASK) >>> X86.LADDR.PDE.SHIFT;
|
var offPDE = (addr & X86.LADDR.PDE.MASK) >>> X86.LADDR.PDE.SHIFT;
|
||||||
var addrPDE = this.cpu.regCR3 + offPDE; // TODO: adding offPDE could be eliminated, along with the busMask mask
|
var addrPDE = this.addrPD + offPDE; // TODO: adding offPDE could be eliminated
|
||||||
var blockPDE = this.aPhysBlocks[(addrPDE & this.busMask) >>> this.blockShift];
|
var blockPDE = this.aPhysBlocks[(addrPDE & this.busMask) >>> this.blockShift];
|
||||||
var pde = blockPDE.readLong(offPDE);
|
var pde = blockPDE.readLong(offPDE);
|
||||||
|
|
||||||
|
|
@ -717,7 +743,7 @@ Bus.prototype.mapPageBlock = function(addr, fWrite)
|
||||||
}
|
}
|
||||||
|
|
||||||
var offPTE = (addr & X86.LADDR.PTE.MASK) >>> X86.LADDR.PTE.SHIFT;
|
var offPTE = (addr & X86.LADDR.PTE.MASK) >>> X86.LADDR.PTE.SHIFT;
|
||||||
var addrPTE = (pde & X86.PTE.FRAME) + offPTE; // TODO: adding offPTE could be eliminated, along with the busMask mask
|
var addrPTE = (pde & X86.PTE.FRAME) + offPTE; // TODO: adding offPTE could be eliminated
|
||||||
var blockPTE = this.aPhysBlocks[(addrPTE & this.busMask) >>> this.blockShift];
|
var blockPTE = this.aPhysBlocks[(addrPTE & this.busMask) >>> this.blockShift];
|
||||||
var pte = blockPTE.readLong(offPTE);
|
var pte = blockPTE.readLong(offPTE);
|
||||||
|
|
||||||
|
|
@ -731,20 +757,42 @@ Bus.prototype.mapPageBlock = function(addr, fWrite)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var addrPhys = (pte & X86.PTE.FRAME) + (addr & X86.LADDR.OFFSET); // TODO: Adding OFFSET could be eliminated, along with the busMask mask
|
var addrPhys = (pte & X86.PTE.FRAME) + (addr & X86.LADDR.OFFSET); // TODO: Adding OFFSET could be eliminated
|
||||||
var blockPhys = this.aPhysBlocks[(addrPhys & this.busMask) >>> this.blockShift];
|
var blockPhys = this.aPhysBlocks[(addrPhys & this.busMask) >>> this.blockShift];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* So we have the block containing the physical memory corresponding to the given linear address.
|
* So we have the block containing the physical memory corresponding to the given linear address.
|
||||||
*
|
*
|
||||||
* Now we create a new "paged" Memory block and record the physical block info using setPhysBlock().
|
* Now we can create a new "paged" Memory block and record the physical block info using setPhysBlock().
|
||||||
*/
|
*/
|
||||||
var addrPage = addr & ~X86.LADDR.OFFSET;
|
var addrPage = addr & ~X86.LADDR.OFFSET;
|
||||||
var blockPage = new Memory(addrPage, 0, this.blockSize, Memory.TYPE.PAGED);
|
var blockPage = new Memory(addrPage, 0, this.blockSize, Memory.TYPE.PAGED);
|
||||||
blockPage.setPhysBlock(blockPage, blockPDE, offPDE, blockPTE, offPTE);
|
blockPage.setPhysBlock(blockPhys, blockPDE, offPDE, blockPTE, offPTE);
|
||||||
|
|
||||||
|
var iBlock = addr >>> this.blockShift;
|
||||||
|
this.aMemBlocks[iBlock] = blockPage;
|
||||||
|
this.aPageBlockNums.push(iBlock);
|
||||||
return blockPage;
|
return blockPage;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* disablePageBlocks()
|
||||||
|
*
|
||||||
|
* Whenever the CPU turns off paging, this function restores the original aMemBlocks.
|
||||||
|
*
|
||||||
|
* @this {Bus}
|
||||||
|
*/
|
||||||
|
Bus.prototype.disablePageBlocks = function()
|
||||||
|
{
|
||||||
|
if (this.aPhysBlocks) {
|
||||||
|
this.aMemBlocks = this.aPhysBlocks;
|
||||||
|
this.aPhysBlocks = null;
|
||||||
|
this.blockUnpaged = null;
|
||||||
|
this.aPageBlockNums = null;
|
||||||
|
}
|
||||||
|
this.addrPD = X86.ADDR_INVALID;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getByte(addr)
|
* getByte(addr)
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -471,6 +471,11 @@ Memory.prototype = {
|
||||||
*/
|
*/
|
||||||
getPageBlock: function(addr, fWrite) {
|
getPageBlock: function(addr, fWrite) {
|
||||||
var block = this.bus.mapPageBlock(addr, fWrite);
|
var block = this.bus.mapPageBlock(addr, fWrite);
|
||||||
|
/*
|
||||||
|
* If mapPageBlock() fails -- which can easily happen if the page is not present or has insufficient
|
||||||
|
* privileges -- then a fault will be triggered and block will be null. We still have to return a block,
|
||||||
|
* but it will be our old "unpaged" self.
|
||||||
|
*/
|
||||||
return block || this;
|
return block || this;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
|
@ -486,9 +491,9 @@ Memory.prototype = {
|
||||||
setPhysBlock: function(blockPhys, blockPDE, offPDE, blockPTE, offPTE) {
|
setPhysBlock: function(blockPhys, blockPDE, offPDE, blockPTE, offPTE) {
|
||||||
this.blockPhys = blockPhys;
|
this.blockPhys = blockPhys;
|
||||||
this.blockPDE = blockPDE;
|
this.blockPDE = blockPDE;
|
||||||
this.iPDE = offPDE >> 2; // convert offPDE into an adw index (iPDE)
|
this.iPDE = offPDE >> 2; // convert offPDE into iPDE (an adw index)
|
||||||
this.blockPTE = blockPTE;
|
this.blockPTE = blockPTE;
|
||||||
this.iPTE = offPTE >> 2; // convert offPTE into an adw index (iPTE)
|
this.iPTE = offPTE >> 2; // convert offPTE into iPTE (an adw index)
|
||||||
this.bitPTEDirty = this.adjustEndian(X86.PTE.ACCESSED | X86.PTE.DIRTY);
|
this.bitPTEDirty = this.adjustEndian(X86.PTE.ACCESSED | X86.PTE.DIRTY);
|
||||||
this.bitPTEAccessed = this.adjustEndian(X86.PTE.ACCESSED);
|
this.bitPTEAccessed = this.adjustEndian(X86.PTE.ACCESSED);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -2379,10 +2379,10 @@ X86CPU.prototype.setMSW = function(w)
|
||||||
w |= (this.regCR0 & X86.CR0.MSW.PE) | X86.CR0.MSW.ON;
|
w |= (this.regCR0 & X86.CR0.MSW.PE) | X86.CR0.MSW.ON;
|
||||||
this.regCR0 = (this.regCR0 & ~X86.CR0.MSW.MASK) | (w & X86.CR0.MSW.MASK);
|
this.regCR0 = (this.regCR0 & ~X86.CR0.MSW.MASK) | (w & X86.CR0.MSW.MASK);
|
||||||
/*
|
/*
|
||||||
* Since the 80286 cannot return to real-mode via this instruction, the only transition
|
* Since the 80286 cannot return to real-mode via this instruction, the only transition we
|
||||||
* we must worry about is to protected-mode. And there's no harm calling setProtMode()
|
* must worry about is to protected-mode. And there's no harm calling setProtMode() if the
|
||||||
* if the CPU is already in protected-mode (we could certainly optimize the call out in that
|
* CPU is already in protected-mode; we could certainly optimize out the call in that case,
|
||||||
* case, but this instruction isn't used frequently enough to warrant it).
|
* but the instruction isn't used frequently enough to warrant it.
|
||||||
*/
|
*/
|
||||||
if (this.regCR0 & X86.CR0.MSW.PE) this.setProtMode(true);
|
if (this.regCR0 & X86.CR0.MSW.PE) this.setProtMode(true);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1319,7 +1319,7 @@ X86.fnLAR = function LAR(dst, src)
|
||||||
/**
|
/**
|
||||||
* fnLCR0(l)
|
* fnLCR0(l)
|
||||||
*
|
*
|
||||||
* This called on behalf of 80386 opcodes only (ie, MOV CR0,reg).
|
* This is called by an 80386 control instruction (ie, MOV CR0,reg).
|
||||||
*
|
*
|
||||||
* TODO: Determine which CR0 bits, if any, cannot be modified by MOV CR0,reg.
|
* TODO: Determine which CR0 bits, if any, cannot be modified by MOV CR0,reg.
|
||||||
*
|
*
|
||||||
|
|
@ -1330,6 +1330,30 @@ X86.fnLCR0 = function LCR0(l)
|
||||||
{
|
{
|
||||||
this.regCR0 = l;
|
this.regCR0 = l;
|
||||||
this.setProtMode();
|
this.setProtMode();
|
||||||
|
if (this.regCR0 & X86.CR0.PG) {
|
||||||
|
this.bus.enablePageBlocks(this.regCR3);
|
||||||
|
} else {
|
||||||
|
this.bus.disablePageBlocks();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fnLCR3(l)
|
||||||
|
*
|
||||||
|
* This is called by an 80386 control instruction (ie, MOV CR3,reg) or an 80386 task switch.
|
||||||
|
*
|
||||||
|
* @this {X86CPU}
|
||||||
|
* @param {number} l
|
||||||
|
*/
|
||||||
|
X86.fnLCR3 = function LCR3(l)
|
||||||
|
{
|
||||||
|
this.regCR3 = l;
|
||||||
|
/*
|
||||||
|
* Normal use of regCR3 involves adding a 0-4K (12-bit) offset to obtain a page directory entry,
|
||||||
|
* so let's ensure that the low 12 bits of regCR3 are always zero.
|
||||||
|
*/
|
||||||
|
this.assert(!(this.regCR3 & X86.LADDR.OFFSET));
|
||||||
|
if (this.regCR0 & X86.CR0.PG) this.bus.enablePageBlocks(this.regCR3);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -3521,7 +3545,7 @@ X86.fnSrcNone = function SrcNone()
|
||||||
*
|
*
|
||||||
* @this {X86CPU}
|
* @this {X86CPU}
|
||||||
* @param {number} nFault
|
* @param {number} nFault
|
||||||
* @param {number} [nError]
|
* @param {number} [nError] (if omitted, no error code will be pushed)
|
||||||
* @param {boolean} [fHalt] will halt the CPU if true *and* a Debugger is loaded
|
* @param {boolean} [fHalt] will halt the CPU if true *and* a Debugger is loaded
|
||||||
*/
|
*/
|
||||||
X86.fnFault = function(nFault, nError, fHalt)
|
X86.fnFault = function(nFault, nError, fHalt)
|
||||||
|
|
@ -3612,7 +3636,7 @@ X86.fnPageFault = function(addr, fPresent, fWrite)
|
||||||
*
|
*
|
||||||
* @this {X86CPU}
|
* @this {X86CPU}
|
||||||
* @param {number} nFault
|
* @param {number} nFault
|
||||||
* @param {number} [nError]
|
* @param {number} [nError] (if omitted, no error code will be reported)
|
||||||
* @param {boolean} [fHalt] true if the CPU should always be halted, false if "it depends"
|
* @param {boolean} [fHalt] true if the CPU should always be halted, false if "it depends"
|
||||||
* @return {boolean|undefined} true to block the fault (often desirable when fHalt is true), otherwise dispatch it
|
* @return {boolean|undefined} true to block the fault (often desirable when fHalt is true), otherwise dispatch it
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ X86.opLOADALL = function LOADALL()
|
||||||
{
|
{
|
||||||
if (this.segCS.cpl) {
|
if (this.segCS.cpl) {
|
||||||
/*
|
/*
|
||||||
* You're not allowed to use LOADALL if the current privilege level is something other than zero
|
* You're not allowed to use LOADALL if the current privilege level is something other than zero.
|
||||||
*/
|
*/
|
||||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0, true);
|
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0, true);
|
||||||
return;
|
return;
|
||||||
|
|
@ -188,7 +188,7 @@ X86.opLOADALL = function LOADALL()
|
||||||
X86.opCLTS = function CLTS()
|
X86.opCLTS = function CLTS()
|
||||||
{
|
{
|
||||||
if (this.segCS.cpl) {
|
if (this.segCS.cpl) {
|
||||||
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0, true);
|
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.regCR0 &= ~X86.CR0.MSW.TS;
|
this.regCR0 &= ~X86.CR0.MSW.TS;
|
||||||
|
|
@ -204,20 +204,35 @@ X86.opCLTS = function CLTS()
|
||||||
* the appropriate control register into a special variable (regXX), which our helper function
|
* the appropriate control register into a special variable (regXX), which our helper function
|
||||||
* (fnMOVxx) will use to replace the decoder's src operand.
|
* (fnMOVxx) will use to replace the decoder's src operand.
|
||||||
*
|
*
|
||||||
|
* From PCMag_Prog_TechRef, p.476: "The 80386 executes the MOV to/from control registers (CRn)
|
||||||
|
* regardless of the setting of the MOD field. The MOD field should be set to 0b11, but an early
|
||||||
|
* 80386 documentation error indicated that the MOD field value was a don't care. Early versions
|
||||||
|
* of the 80486 detect a MOD != 0b11 as an illegal opcode. This was changed in later versions to
|
||||||
|
* ignore the value of MOD. Assemblers that generate MOD != 0b11 for these instructions will fail
|
||||||
|
* on some 80486s."
|
||||||
|
*
|
||||||
* @this {X86CPU}
|
* @this {X86CPU}
|
||||||
*/
|
*/
|
||||||
X86.opMOVrc = function MOVrc()
|
X86.opMOVrc = function MOVrc()
|
||||||
{
|
{
|
||||||
var bModRM = this.getIPByte() | 0xc0;
|
|
||||||
/*
|
/*
|
||||||
* Unlike, say, opcode 0x8C (MOV word,sr), this opcode supports only registers, not memory;
|
* We address the MOD field problem (see above) by coercing it to 0b11 (0xc0), regardless.
|
||||||
* however, the 80386 apparently ignores the mod bits, treating any combination as if it was 0xc0.
|
|
||||||
*
|
*
|
||||||
if ((bModRM & 0xc0) != 0xc0) {
|
* TODO: One issue not clearly addressed is if, when an assembler/compiler generated a bogus MOD value,
|
||||||
X86.opInvalid.call(this);
|
* it also generated the additional displacement bytes, if any, that would typically accompany such a MOD
|
||||||
|
* value. I assume not.
|
||||||
|
*/
|
||||||
|
var bModRM = this.getIPByte() | 0xc0;
|
||||||
|
|
||||||
|
if (this.segCS.cpl) {
|
||||||
|
/*
|
||||||
|
* You're not allowed to read control registers if the current privilege level is not zero
|
||||||
|
* (TODO: I'm issuing this AFTER fetching the ModRM byte, but I assume it makes no difference).
|
||||||
|
*/
|
||||||
|
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
var reg = (bModRM & 0x38) >> 3;
|
var reg = (bModRM & 0x38) >> 3;
|
||||||
switch(reg) {
|
switch(reg) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
|
|
@ -250,26 +265,40 @@ X86.opMOVrc = function MOVrc()
|
||||||
*
|
*
|
||||||
* op=0x0F,0x22 (MOV creg,reg)
|
* op=0x0F,0x22 (MOV creg,reg)
|
||||||
*
|
*
|
||||||
* NOTE: Since the ModRM decoders deal only with general-purpose registers, we have to
|
* NOTE: Since the ModRM decoders deal only with general-purpose registers, we have to make a note
|
||||||
* make a note of which general-purpose register will be overwritten, so that we can restore it
|
* of which general-purpose register will be overwritten, so that we can restore it after moving the
|
||||||
* after moving the modified value to the correct control register.
|
* modified value to the correct control register.
|
||||||
|
*
|
||||||
|
* From PCMag_Prog_TechRef, p.476: "The 80386 executes the MOV to/from control registers (CRn)
|
||||||
|
* regardless of the setting of the MOD field. The MOD field should be set to 0b11, but an early
|
||||||
|
* 80386 documentation error indicated that the MOD field value was a don't care. Early versions
|
||||||
|
* of the 80486 detect a MOD != 0b11 as an illegal opcode. This was changed in later versions to
|
||||||
|
* ignore the value of MOD. Assemblers that generate MOD != 0b11 for these instructions will fail
|
||||||
|
* on some 80486s."
|
||||||
*
|
*
|
||||||
* @this {X86CPU}
|
* @this {X86CPU}
|
||||||
*/
|
*/
|
||||||
X86.opMOVcr = function MOVcr()
|
X86.opMOVcr = function MOVcr()
|
||||||
{
|
{
|
||||||
var temp;
|
var temp;
|
||||||
var bModRM = this.getIPByte() | 0xc0;
|
|
||||||
/*
|
/*
|
||||||
* Unlike, say, opcode 0x8E (MOV sreg,word), this opcode supports only registers, not memory;
|
* We address the MOD field problem (see above) by coercing it to 0b11 (0xc0), regardless.
|
||||||
* however, the 80386 apparently ignores the mod bits, treating any combination as if it was 0xc0.
|
|
||||||
* TODO: Verify.
|
|
||||||
*
|
*
|
||||||
if ((bModRM & 0xc0) != 0xc0) {
|
* TODO: One issue not clearly addressed is if, when an assembler/compiler generated a bogus MOD value,
|
||||||
X86.opInvalid.call(this);
|
* it also generated the additional displacement bytes, if any, that would typically accompany such a MOD
|
||||||
|
* value. I assume not.
|
||||||
|
*/
|
||||||
|
var bModRM = this.getIPByte() | 0xc0;
|
||||||
|
|
||||||
|
if (this.segCS.cpl) {
|
||||||
|
/*
|
||||||
|
* You're not allowed to write control registers if the current privilege level is not zero
|
||||||
|
* (TODO: I'm issuing this AFTER fetching the ModRM byte, but I assume it makes no difference).
|
||||||
|
*/
|
||||||
|
X86.fnFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
var reg = (bModRM & 0x38) >> 3;
|
var reg = (bModRM & 0x38) >> 3;
|
||||||
switch(reg) {
|
switch(reg) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
|
|
@ -306,13 +335,9 @@ X86.opMOVcr = function MOVcr()
|
||||||
this.regEDX = temp;
|
this.regEDX = temp;
|
||||||
break;
|
break;
|
||||||
case 0x3:
|
case 0x3:
|
||||||
this.regCR3 = this.regEBX;
|
reg = this.regEBX;
|
||||||
/*
|
|
||||||
* Normal use of regCR3 involves adding a 0-4K (12-bit) offset to obtain a page directory entry, so
|
|
||||||
* let's ensure that the low 12 bits of regCR3 are always zero.
|
|
||||||
*/
|
|
||||||
this.assert(!(this.regCR3 & X86.LADDR.OFFSET));
|
|
||||||
this.regEBX = temp;
|
this.regEBX = temp;
|
||||||
|
X86.fnLCR3.call(this, reg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -414,6 +414,8 @@ X86Seg.prototype.checkWriteProtDisallowed = function checkWriteProtDisallowed(of
|
||||||
* Of course, that all could have been avoided if IBM had heeded Intel's advice and not used Intel-reserved IDT
|
* Of course, that all could have been avoided if IBM had heeded Intel's advice and not used Intel-reserved IDT
|
||||||
* entries for PC interrupts.
|
* entries for PC interrupts.
|
||||||
*
|
*
|
||||||
|
* TODO: Add 80386 TSS support (including CR3 support).
|
||||||
|
*
|
||||||
* @this {X86Seg}
|
* @this {X86Seg}
|
||||||
* @param {number} selNew
|
* @param {number} selNew
|
||||||
* @param {boolean} fNest is true if nesting, false if un-nesting
|
* @param {boolean} fNest is true if nesting, false if un-nesting
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue