More PAGEBLOCKS support

This commit is contained in:
Jeff Parsons 2015-04-29 09:45:03 -07:00 committed by jeffpar
commit 453d84c6d8
7 changed files with 252 additions and 48 deletions

View file

@ -39,6 +39,7 @@ if (typeof module !== 'undefined') {
var Memory = require("./memory");
var Messages = require("./messages");
var State = require("./state");
var X86 = require("./x86");
}
/**
@ -67,8 +68,8 @@ if (typeof module !== 'undefined') {
* @constructor
* @extends Component
* @param {Object} parmsBus
* @param {X86CPU|Component} cpu
* @param {Debugger|Component} dbg
* @param {X86CPU} cpu
* @param {Debugger} dbg
*/
function Bus(parmsBus, cpu, dbg)
{
@ -689,20 +690,59 @@ Bus.prototype.enablePageBlocks = function(addrPD)
};
/**
* mapPageBlock(addr)
* mapPageBlock(addr, fWrite)
*
* If addr is a valid linear address, then we must obtain the following information:
* Locate the corresponding physical PDE, PTE and memory blocks for the given linear address.
*
* (1) the physical Memory object corresponding to the linear address in addr
* (2) the physical Memory object containing that memory's PTE
* (3) the offset within the preceding physical Memory object of that memory's PTE
*
* Item (1) allows us to convert an "unpaged" Memory block in to a "paged" Memory block, and the combination of
* (2) and (3) allows us to efficiently update the PTE whenever the page is accesssed and/or modified.
* @this {Bus}
* @param {number} addr is a linear address
* @param {boolean} fWrite (true if called for a write, false if for a read)
* @return {Memory|null}
*/
Bus.prototype.mapPageBlock = function(addr)
Bus.prototype.mapPageBlock = function(addr, fWrite)
{
return null;
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 blockPDE = this.aPhysBlocks[(addrPDE & this.busMask) >>> this.blockShift];
var pde = blockPDE.readLong(offPDE);
if (!(pde & X86.PTE.PRESENT)) {
X86.fnPageFault.call(this.cpu, addr, false, fWrite);
return null;
}
if (!(pde & X86.PTE.USER) && this.cpu.segCS.cpl == 3) {
X86.fnPageFault.call(this.cpu, addr, true, fWrite);
return null;
}
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 blockPTE = this.aPhysBlocks[(addrPTE & this.busMask) >>> this.blockShift];
var pte = blockPTE.readLong(offPTE);
if (!(pte & X86.PTE.PRESENT)) {
X86.fnPageFault.call(this.cpu, addr, false, fWrite);
return null;
}
if (!(pte & X86.PTE.USER) && this.cpu.segCS.cpl == 3) {
X86.fnPageFault.call(this.cpu, addr, true, fWrite);
return null;
}
var addrPhys = (pte & X86.PTE.FRAME) + (addr & X86.LADDR.OFFSET); // TODO: Adding OFFSET could be eliminated, along with the busMask mask
var blockPhys = this.aPhysBlocks[(addrPhys & this.busMask) >>> this.blockShift];
/*
* 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().
*/
var addrPage = addr & ~X86.LADDR.OFFSET;
var blockPage = new Memory(addrPage, 0, this.blockSize, Memory.TYPE.PAGED);
blockPage.setPhysBlock(blockPage, blockPDE, offPDE, blockPTE, offPTE);
return blockPage;
};
/**

View file

@ -140,13 +140,17 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
/*
* Find the appropriate CPU (and Debugger and Control Panel, if any)
*
* CLOSURE COMPILER TIP: To override the type of a right-hand expression (as we need to do here,
* where we know getComponentByType() will only return an X86CPU object or null), wrap the expression
* in parentheses. I never knew this until I stumbled across it in "Closure: The Definitive Guide".
*/
this.cpu = Component.getComponentByType("CPU", this.id);
this.cpu = /** @type {X86CPU} */ (Component.getComponentByType("CPU", this.id));
if (!this.cpu) {
Component.error("Unable to find CPU component");
return;
}
this.dbg = Component.getComponentByType("Debugger", this.id);
this.dbg = /** @type {Debugger} */ (Component.getComponentByType("Debugger", this.id));
/*
* Initialize the Bus component

View file

@ -149,6 +149,7 @@ if (typeof module !== 'undefined') {
global.BUGS_8086 = BUGS_8086;
global.I386 = I386;
global.COMPAQ386 = COMPAQ386;
global.PAGEBLOCKS = PAGEBLOCKS;
/*
* TODO: When we're "required" by Node, should we return anything via module.exports?
*/

View file

@ -36,6 +36,7 @@ if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var Component = require("../../shared/lib/component");
var Messages = require("./messages");
var X86 = require("./x86");
}
/**
@ -201,14 +202,15 @@ function Memory(addr, used, size, type, controller, bus)
* empty (that is, their data arrays are uninitialized) and the memory type is NONE.
*/
Memory.TYPE = {
NONE: 0,
RAM: 1,
ROM: 2,
VIDEO: 3,
CTRL: 4,
UNPAGED:5,
NAMES: ["NONE", "RAM", "ROM", "VIDEO", "H/W"],
COLORS: ["black", "blue", "green", "cyan"]
NONE: 0,
RAM: 1,
ROM: 2,
VIDEO: 3,
CTRL: 4,
UNPAGED: 5,
PAGED: 6,
NAMES: ["NONE", "RAM", "ROM", "VIDEO", "H/W", "UNPAGED", "PAGED"],
COLORS: ["black", "blue", "green", "cyan"]
};
/*
@ -232,8 +234,8 @@ Memory.prototype = {
clone: function(mem, type) {
/*
* Original memory block IDs are even; cloned memory block IDs are odd;
* the original ID of the current block is lost, but that's OK, since it was
* presumably produced merely to become a clone.
* the original ID of the current block is lost, but that's OK, since it was presumably
* produced merely to become a clone.
*/
this.id = mem.id | 0x1;
this.used = mem.used;
@ -361,7 +363,17 @@ Memory.prototype = {
* @param {boolean} [fDirect]
*/
setAccess: function(afn, fDirect) {
afn = afn || (this.type == Memory.TYPE.UNPAGED? Memory.afnUnpaged : Memory.afnNone);
if (!afn) {
if (this.type == Memory.TYPE.UNPAGED) {
afn = Memory.afnUnpaged;
}
else if (this.type == Memory.TYPE.PAGED) {
afn = Memory.afnPaged;
} else {
Component.assert(this.type == Memory.TYPE.NONE);
afn = Memory.afnNone;
}
}
if (fDirect === undefined) fDirect = true; // TODO: Verify that this is desired default behavior
this.setReadAccess(afn, fDirect);
this.setWriteAccess(afn, fDirect);
@ -424,7 +436,7 @@ Memory.prototype = {
* setDebugger(dbg, addr, size)
*
* @this {Memory}
* @param {Debugger|Component} dbg
* @param {Debugger} dbg
* @param {number} addr of block
* @param {number} size of block
*/
@ -437,14 +449,48 @@ Memory.prototype = {
}
},
/**
* setPageBlock(addr)
* adjustEndian(dw)
*
* @this {Memory}
* @param {number} dw
* @return {number}
*/
adjustEndian: function(dw) {
if (TYPEDARRAYS && !littleEndian) {
dw = (dw << 24) | ((dw << 8) & 0x00ff0000) | ((dw >> 8) & 0x0000ff00) | (dw >>> 24);
}
return dw;
},
/**
* getPageBlock(addr, fWrite)
*
* @this {Memory}
* @param {number} addr
* @param {boolean} fWrite (true if called for a write, false if for a read)
* @return {Memory}
*/
setPageBlock: function(addr) {
return this.bus.mapPageBlock(addr);
getPageBlock: function(addr, fWrite) {
var block = this.bus.mapPageBlock(addr, fWrite);
return block || this;
},
/**
* setPhysBlock(blockPhys, blockPDE, offPDE, blockPTE, offPTE)
*
* @this {Memory}
* @param {Memory} blockPhys
* @param {Memory} blockPDE
* @param {number} offPDE
* @param {Memory} blockPTE
* @param {number} offPTE
*/
setPhysBlock: function(blockPhys, blockPDE, offPDE, blockPTE, offPTE) {
this.blockPhys = blockPhys;
this.blockPDE = blockPDE;
this.iPDE = offPDE >> 2; // convert offPDE into an adw index (iPDE)
this.blockPTE = blockPTE;
this.iPTE = offPTE >> 2; // convert offPTE into an adw index (iPTE)
this.bitPTEDirty = this.adjustEndian(X86.PTE.ACCESSED | X86.PTE.DIRTY);
this.bitPTEAccessed = this.adjustEndian(X86.PTE.ACCESSED);
},
/**
* addBreakpoint(off, fWrite)
@ -679,11 +725,6 @@ Memory.prototype = {
var idw = off >> 2;
var nShift = (off & 0x3) << 3;
if (nShift < 24) {
/*
* 0: 0xffff0000
* 8: 0xff0000ff
* 16: 0x0000ffff
*/
this.adw[idw] = (this.adw[idw] & ~(0xffff << nShift)) | (w << nShift);
} else {
this.adw[idw] = (this.adw[idw] & 0x00ffffff) | (w << 24);
@ -714,11 +755,6 @@ Memory.prototype = {
if (!nShift) {
this.adw[idw] = l;
} else {
/*
* 8: 0xffffff00
* 16: 0xffff0000
* 24: 0xff000000
*/
var mask = (0xffffffff|0) << nShift;
this.adw[idw] = (this.adw[idw] & ~mask) | (l << nShift);
idw++;
@ -815,6 +851,84 @@ Memory.prototype = {
}
this.writeLongDirect(off, l);
},
/**
* readBytePaged(off, addr)
*
* @this {Memory}
* @param {number} off
* @param {number} addr
* @return {number}
*/
readBytePaged: function readBytePaged(off, addr) {
this.blockPDE.adw[this.iPDE] |= this.bitPTEAccessed;
this.blockPTE.adw[this.iPTE] |= this.bitPTEAccessed;
return this.blockPhys.readByte(off, addr);
},
/**
* readShortPaged(off, addr)
*
* @this {Memory}
* @param {number} off
* @param {number} addr
* @return {number}
*/
readShortPaged: function readShortPaged(off, addr) {
this.blockPDE.adw[this.iPDE] |= this.bitPTEAccessed;
this.blockPTE.adw[this.iPTE] |= this.bitPTEAccessed;
return this.blockPhys.readShort(off, addr);
},
/**
* readLongPaged(off, addr)
*
* @this {Memory}
* @param {number} off
* @param {number} addr
* @return {number}
*/
readLongPaged: function readLongPaged(off, addr) {
this.blockPDE.adw[this.iPDE] |= this.bitPTEAccessed;
this.blockPTE.adw[this.iPTE] |= this.bitPTEAccessed;
return this.blockPhys.readLong(off, addr);
},
/**
* writeBytePaged(off, b, addr)
*
* @this {Memory}
* @param {number} off
* @param {number} b
* @param {number} addr
*/
writeBytePaged: function writeBytePaged(off, b, addr) {
this.blockPDE.adw[this.iPDE] |= this.bitPTEAccessed;
this.blockPTE.adw[this.iPTE] |= this.bitPTEDirty;
this.blockPhys.writeByte(off, b, addr);
},
/**
* writeShortPaged(off, w, addr)
*
* @this {Memory}
* @param {number} off
* @param {number} w
* @param {number} addr
*/
writeShortPaged: function writeShortPaged(off, w, addr) {
this.blockPDE.adw[this.iPDE] |= this.bitPTEAccessed;
this.blockPTE.adw[this.iPTE] |= this.bitPTEDirty;
this.blockPhys.writeShort(off, w, addr);
},
/**
* writeLongPaged(off, l, addr)
*
* @this {Memory}
* @param {number} off
* @param {number} l
* @param {number} addr
*/
writeLongPaged: function writeLongPaged(off, l, addr) {
this.blockPDE.adw[this.iPDE] |= this.bitPTEAccessed;
this.blockPTE.adw[this.iPTE] |= this.bitPTEDirty;
this.blockPhys.writeLong(off, l, addr);
},
/**
* readByteUnpaged(off, addr)
*
@ -824,7 +938,7 @@ Memory.prototype = {
* @return {number}
*/
readByteUnpaged: function readByteUnpaged(off, addr) {
return this.setPageBlock(addr).readByte(off, addr);
return this.getPageBlock(addr, false).readByte(off, addr);
},
/**
* readShortUnpaged(off, addr)
@ -835,7 +949,7 @@ Memory.prototype = {
* @return {number}
*/
readShortUnpaged: function readShortUnpaged(off, addr) {
return this.setPageBlock(addr).readShort(off, addr);
return this.getPageBlock(addr, false).readShort(off, addr);
},
/**
* readLongUnpaged(off, addr)
@ -846,7 +960,7 @@ Memory.prototype = {
* @return {number}
*/
readLongUnpaged: function readLongUnpaged(off, addr) {
return this.setPageBlock(addr).readLong(off, addr);
return this.getPageBlock(addr, false).readLong(off, addr);
},
/**
* writeByteUnpaged(off, b, addr)
@ -857,7 +971,7 @@ Memory.prototype = {
* @param {number} addr
*/
writeByteUnpaged: function writeByteUnpaged(off, b, addr) {
this.setPageBlock(addr).writeByte(off, b, addr);
this.getPageBlock(addr, true).writeByte(off, b, addr);
},
/**
* writeShortUnpaged(off, w, addr)
@ -868,7 +982,7 @@ Memory.prototype = {
* @param {number} addr
*/
writeShortUnpaged: function writeShortUnpaged(off, w, addr) {
this.setPageBlock(addr).writeShort(off, w, addr);
this.getPageBlock(addr, true).writeShort(off, w, addr);
},
/**
* writeLongUnpaged(off, l, addr)
@ -879,7 +993,7 @@ Memory.prototype = {
* @param {number} addr
*/
writeLongUnpaged: function writeLongUnpaged(off, l, addr) {
this.setPageBlock(addr).writeLong(off, l, addr);
this.getPageBlock(addr, true).writeLong(off, l, addr);
},
/**
* readByteBigEndian(off, addr)
@ -1132,8 +1246,8 @@ Memory.prototype = {
};
/*
* This is the effective definition of afnNone, but we need not fully define it, because setAccess() already
* uses these defaults when any of the 6 handlers (ie, 3 read handlers followed by 3 write handlers) are undefined.
* This is the effective definition of afnNone, but we need not fully define it, because setAccess()
* uses these defaults when any of the 6 handlers (ie, 3 read handlers and 3 write handlers) are undefined.
*
Memory.afnNone = [Memory.prototype.readNone, Memory.prototype.readShortDefault, Memory.prototype.readLongDefault, Memory.prototype.writeNone, Memory.prototype.writeShortDefault, Memory.prototype.writeLongDefault];
*/
@ -1143,7 +1257,7 @@ Memory.afnMemory = [Memory.prototype.readByteMemory, Memory.prototyp
Memory.afnChecked = [Memory.prototype.readByteChecked, Memory.prototype.readShortChecked, Memory.prototype.readLongChecked, Memory.prototype.writeByteChecked, Memory.prototype.writeShortChecked, Memory.prototype.writeLongChecked];
if (PAGEBLOCKS) {
// Memory.afnPaged = [Memory.prototype.readBytePaged, Memory.prototype.readShortPaged, Memory.prototype.readLongPaged, Memory.prototype.writeBytePaged, Memory.prototype.writeShortPaged, Memory.prototype.writeLongPaged];
Memory.afnPaged = [Memory.prototype.readBytePaged, Memory.prototype.readShortPaged, Memory.prototype.readLongPaged, Memory.prototype.writeBytePaged, Memory.prototype.writeShortPaged, Memory.prototype.writeLongPaged];
Memory.afnUnpaged = [Memory.prototype.readByteUnpaged, Memory.prototype.readShortUnpaged, Memory.prototype.readLongUnpaged, Memory.prototype.writeByteUnpaged, Memory.prototype.writeShortUnpaged, Memory.prototype.writeLongUnpaged];
}

View file

@ -171,6 +171,25 @@ var X86 = {
},
INVALID: 0 // use X86.DESC.INVALID for invalid DESC values
},
LADDR: { // linear address
PDE: { // index of page directory entry
MASK: 0xffc00000,
SHIFT: 20 // (addr & DIR.MASK) >>> DIR.SHIFT yields a page directory offset (ie, index * 4)
},
PTE: { // index of page table entry
MASK: 0x003ff000,
SHIFT: 10 // (addr & PAGE.MASK) >>> PAGE.SHIFT yields a page table offset (ie, index * 4)
},
OFFSET: 0x00000fff
},
PTE: {
FRAME: 0xfffff000,
DIRTY: 0x00000040, // page has been modified
ACCESSED: 0x00000020, // page has been accessed
USER: 0x00000004, // set for user level (CPL 3), clear for supervisor level (CPL 0-2)
READWRITE: 0x00000002, // set for read/write, clear for read-only (affects CPL 3 only)
PRESENT: 0x00000001 // set for present page, clear for not-present page
},
TSS: {
PREV_TSS: 0x00,
CPL0_SP: 0x02, // start of values altered by task switches
@ -234,6 +253,7 @@ var X86 = {
NP_FAULT: 0x0B, // Not Present Fault (protected-mode only)
SS_FAULT: 0x0C, // Stack Fault (protected-mode only)
GP_FAULT: 0x0D, // General Protection Fault
PG_FAULT: 0x0E, // Page Fault
MF_FAULT: 0x10 // Math Fault (see ESC or WAIT)
},
ERRCODE: {

View file

@ -3461,6 +3461,26 @@ X86.fnFault = function(nFault, nError, fHalt)
this.opFlags &= ~(X86.OPFLAG.NOREAD | X86.OPFLAG.NOWRITE);
};
/**
* fnPageFault(addr, fPresent, fWrite)
*
* Helper to dispatch page faults.
*
* @this {X86CPU}
* @param {number} addr
* @param {boolean} fPresent
* @param {boolean} fWrite
*/
X86.fnPageFault = function(addr, fPresent, fWrite)
{
this.regCR2 = addr;
var nError = 0;
if (fPresent) nError |= X86.PTE.PRESENT;
if (fWrite) nError |= X86.PTE.READWRITE;
if (this.segCS.cpl == 3) nError |= X86.PTE.USER;
X86.fnFault.call(this, X86.EXCEPTION.PG_FAULT, nError);
};
/**
* fnFaultMessage()
*

View file

@ -307,6 +307,11 @@ X86.opMOVcr = function MOVcr()
break;
case 0x3:
this.regCR3 = 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;
break;
}