Fixed Closure Compiler build (the compiler gets easily confused when objects in different class hierarchies have identical method names)
This commit is contained in:
parent
231bc201e7
commit
39af7dbe32
6 changed files with 145 additions and 65 deletions
|
|
@ -61,9 +61,10 @@ if (NODE) {
|
|||
* addMemory(). If the component needs something more than simple read/write storage,
|
||||
* it must provide a controller with getMemoryBuffer() and getMemoryAccess() methods.
|
||||
*
|
||||
* By contrast, all port (I/O) operations are defined by external handlers; they register
|
||||
* with us, and we manage those registrations, as well as support for I/O breakpoints,
|
||||
* but unlike memory accesses, we're not involved with port data accesses.
|
||||
* All port (I/O) operations are defined by external handlers; they register with us,
|
||||
* and we manage those registrations and provide support for I/O breakpoints, but the
|
||||
* only default I/O behavior we provide is ignoring writes to any unregistered output
|
||||
* ports and returning 0xff from any unregistered input ports.
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
|
|
@ -1266,7 +1267,7 @@ Bus.prototype.getSymbol = function(addr, fNearest)
|
|||
};
|
||||
|
||||
/**
|
||||
* saveMemory()
|
||||
* saveMemory(fAll)
|
||||
*
|
||||
* The only memory blocks we save are those marked as dirty, but most likely all of RAM will have been marked dirty,
|
||||
* and even if our dirty-memory flags were as smart as our dirty-sector flags (ie, were set only when a write changed
|
||||
|
|
@ -1291,17 +1292,17 @@ Bus.prototype.getSymbol = function(addr, fNearest)
|
|||
* helper methods compress() and decompress() to create and expand the compressed data arrays.
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {boolean} [fAll] (true to save all non-ROM memory blocks, regardless of their dirty flags)
|
||||
* @return {Array} a
|
||||
*/
|
||||
Bus.prototype.saveMemory = function()
|
||||
Bus.prototype.saveMemory = function(fAll)
|
||||
{
|
||||
var i = 0;
|
||||
var a = [];
|
||||
|
||||
/*
|
||||
* A quick-and-dirty work-around for 32-bit bus machines, to ensure that all blocks in the 2nd Mb are
|
||||
* mapped in before we save. We do this by forcing A20 on, and then turning it back off again before we
|
||||
* leave.
|
||||
* mapped in before we save. We do this by forcing A20 on, and then turning it off again before we leave.
|
||||
*/
|
||||
var fA20 = this.getA20();
|
||||
if (!fA20) this.setA20(true);
|
||||
|
|
@ -1313,7 +1314,7 @@ Bus.prototype.saveMemory = function()
|
|||
* the memory blocks (eg, video memory), and while cleanMemory() will clear a dirty block's fDirty flag,
|
||||
* it also sets the dirty block's fDirtyEver flag, which is left set for the lifetime of the machine.
|
||||
*/
|
||||
if (block.fDirty || block.fDirtyEver) {
|
||||
if (fAll && block.type != Memory.TYPE.ROM || block.fDirty || block.fDirtyEver) {
|
||||
a[i++] = iBlock;
|
||||
a[i++] = State.compress(block.save());
|
||||
}
|
||||
|
|
@ -1336,7 +1337,7 @@ Bus.prototype.saveMemory = function()
|
|||
* it was using when it's restored. And since the CPU is guaranteed to be the last
|
||||
* component to be restored, all those blocks (and their attributes) should be in place now.
|
||||
*
|
||||
* See saveMemory() for a description of how the memory block contents are saved.
|
||||
* See saveMemory() for more information on how the memory block contents are saved.
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {Array} a
|
||||
|
|
@ -1466,12 +1467,11 @@ Bus.prototype.checkPortInputNotify = function(port, size, addrLIP)
|
|||
var dataPort = maskPort;
|
||||
|
||||
/*
|
||||
* TODO: We need to decide what to do about 8-bit I/O to a 16-bit port
|
||||
* (ditto for 16-bit I/O to a 32-bit port). We probably should pass the
|
||||
* size through to the aNotify[0] handler, and let it decide what to do,
|
||||
* but I don't feel like changing all the I/O handlers right now. The
|
||||
* good news, at least, is that the 8-bit handlers would not have to do
|
||||
* anything special. This assert will warn us if this is a pressing need.
|
||||
* TODO: We need to decide what to do about 8-bit I/O to a 16-bit port (ditto for 16-bit I/O
|
||||
* to a 32-bit port). We probably should pass the size through to the aNotify[0] handler,
|
||||
* and let it decide what to do, but I don't feel like changing all the I/O handlers right now.
|
||||
* The good news, at least, is that the 8-bit handlers would not have to do anything special.
|
||||
* This assert will warn us if this is a pressing need.
|
||||
*/
|
||||
this.assert(size >= sizePort);
|
||||
|
||||
|
|
@ -1625,12 +1625,11 @@ Bus.prototype.checkPortOutputNotify = function(port, size, data, addrLIP)
|
|||
var dataPort = (data >>>= shift) & maskPort;
|
||||
|
||||
/*
|
||||
* TODO: We need to decide what to do about 8-bit I/O to a 16-bit port
|
||||
* (ditto for 16-bit I/O to a 32-bit port). We probably should pass the
|
||||
* size through to the aNotify[0] handler, and let it decide what to do,
|
||||
* but I don't feel like changing all the I/O handlers right now. The
|
||||
* good news, at least, is that the 8-bit handlers would not have to do
|
||||
* anything special. This assert will warn us if this is a pressing need.
|
||||
* TODO: We need to decide what to do about 8-bit I/O to a 16-bit port (ditto for 16-bit I/O
|
||||
* to a 32-bit port). We probably should pass the size through to the aNotify[0] handler,
|
||||
* and let it decide what to do, but I don't feel like changing all the I/O handlers right now.
|
||||
* The good news, at least, is that the 8-bit handlers would not have to do anything special.
|
||||
* This assert will warn us if this is a pressing need.
|
||||
*/
|
||||
this.assert(size >= sizePort);
|
||||
|
||||
|
|
|
|||
|
|
@ -256,6 +256,17 @@ Memory.adjustEndian = function(dw) {
|
|||
Memory.prototype = {
|
||||
constructor: Memory,
|
||||
parent: null,
|
||||
/**
|
||||
* init(addr)
|
||||
*
|
||||
* Quick reinitializer when reusing a Memory block.
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} addr
|
||||
*/
|
||||
init: function(addr) {
|
||||
this.addr = addr;
|
||||
},
|
||||
/**
|
||||
* clone(mem, type)
|
||||
*
|
||||
|
|
@ -501,6 +512,7 @@ Memory.prototype = {
|
|||
* @param {number} offPTE
|
||||
*/
|
||||
setPhysBlock: function(blockPhys, blockPDE, offPDE, blockPTE, offPTE) {
|
||||
this.blockPhys = blockPhys;
|
||||
this.blockPDE = blockPDE;
|
||||
this.iPDE = offPDE >> 2; // convert offPDE into iPDE (an adw index)
|
||||
this.blockPTE = blockPTE;
|
||||
|
|
@ -516,9 +528,9 @@ Memory.prototype = {
|
|||
this.adw = blockPhys.adw;
|
||||
this.setAccess(Memory.afnPagedLE);
|
||||
} else {
|
||||
this.blockPhys = blockPhys;
|
||||
this.bitPTEAccessed = blockPhys? Memory.adjustEndian(X86.PTE.ACCESSED) : 0;
|
||||
this.bitPTEDirty = blockPhys? Memory.adjustEndian(X86.PTE.ACCESSED | X86.PTE.DIRTY) : 0;
|
||||
this.setAccess(Memory.afnPaged);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
|
@ -1231,7 +1243,14 @@ Memory.prototype = {
|
|||
this.ab[off] = b;
|
||||
this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED;
|
||||
this.blockPTE.adw[this.iPTE] |= X86.PTE.ACCESSED | X86.PTE.DIRTY;
|
||||
this.fDirty = true;
|
||||
/*
|
||||
* NOTE: Technically, we should be setting the fDirty flag on blockPDE and blockPTE as well, but let's
|
||||
* consider the two sole uses of fDirty. First, we have cleanMemory(), which is currently used only by
|
||||
* the Video component, and video memory should never contain page directories or page tables, so no
|
||||
* worries there. Second, we have saveMemory(), but the CPU now asks that function to save all physical
|
||||
* memory blocks whenever paging is enabled, so no worries there either.
|
||||
*/
|
||||
this.blockPhys.fDirty = true;
|
||||
},
|
||||
/**
|
||||
* writeShortBE(off, w, addr)
|
||||
|
|
@ -1287,7 +1306,14 @@ Memory.prototype = {
|
|||
}
|
||||
this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED;
|
||||
this.blockPTE.adw[this.iPTE] |= X86.PTE.ACCESSED | X86.PTE.DIRTY;
|
||||
this.fDirty = true;
|
||||
/*
|
||||
* NOTE: Technically, we should be setting the fDirty flag on blockPDE and blockPTE as well, but let's
|
||||
* consider the two sole uses of fDirty. First, we have cleanMemory(), which is currently used only by
|
||||
* the Video component, and video memory should never contain page directories or page tables, so no
|
||||
* worries there. Second, we have saveMemory(), but the CPU now asks that function to save all physical
|
||||
* memory blocks whenever paging is enabled, so no worries there either.
|
||||
*/
|
||||
this.blockPhys.fDirty = true;
|
||||
},
|
||||
/**
|
||||
* writeLongBE(off, l, addr)
|
||||
|
|
@ -1347,7 +1373,14 @@ Memory.prototype = {
|
|||
}
|
||||
this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED;
|
||||
this.blockPTE.adw[this.iPTE] |= X86.PTE.ACCESSED | X86.PTE.DIRTY;
|
||||
this.fDirty = true;
|
||||
/*
|
||||
* NOTE: Technically, we should be setting the fDirty flag on blockPDE and blockPTE as well, but let's
|
||||
* consider the two sole uses of fDirty. First, we have cleanMemory(), which is currently used only by
|
||||
* the Video component, and video memory should never contain page directories or page tables, so no
|
||||
* worries there. Second, we have saveMemory(), but the CPU now asks that function to save all physical
|
||||
* memory blocks whenever paging is enabled, so no worries there either.
|
||||
*/
|
||||
this.blockPhys.fDirty = true;
|
||||
},
|
||||
/**
|
||||
* readBackTrackNone(off)
|
||||
|
|
|
|||
|
|
@ -278,9 +278,7 @@ State.prototype = {
|
|||
if (s) {
|
||||
this[this.id] = s;
|
||||
this.fLoaded = true;
|
||||
if (DEBUG && this.messageEnabled()) {
|
||||
this.printMessage("localStorage(" + this.key + "): " + s.length + " bytes loaded");
|
||||
}
|
||||
if (DEBUG) this.printString("localStorage(" + this.key + "): " + s.length + " bytes loaded");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -317,9 +315,7 @@ State.prototype = {
|
|||
if (web.hasLocalStorage()) {
|
||||
var s = JSON.stringify(this[this.id]);
|
||||
if (web.setLocalStorageItem(this.key, s)) {
|
||||
if (DEBUG && this.messageEnabled()) {
|
||||
this.printMessage("localStorage(" + this.key + "): " + s.length + " bytes stored");
|
||||
}
|
||||
if (DEBUG) this.printString("localStorage(" + this.key + "): " + s.length + " bytes stored");
|
||||
} else {
|
||||
/*
|
||||
* WARNING: Because browsers tend to disable all alerts() during an "unload" operation,
|
||||
|
|
@ -377,40 +373,24 @@ State.prototype = {
|
|||
var sKey = aKeys[i];
|
||||
if (sKey && (fAll || sKey.substr(0, this.key.length) == this.key)) {
|
||||
web.removeLocalStorageItem(sKey);
|
||||
if (DEBUG && this.messageEnabled()) {
|
||||
this.printMessage("localStorage(" + sKey + ") removed");
|
||||
}
|
||||
if (DEBUG) this.printString("localStorage(" + sKey + ") removed");
|
||||
aKeys.splice(i, 1);
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* messageEnabled(bitsMessage)
|
||||
* printString(s)
|
||||
*
|
||||
* @this {State}
|
||||
* @param {number} [bitsMessage] is one or more Messages category flag(s)
|
||||
* @return {boolean}
|
||||
* @param {string} s is any caller-defined string
|
||||
*/
|
||||
messageEnabled: function(bitsMessage) {
|
||||
if (DEBUGGER && this.dbg) {
|
||||
if (bitsMessage == null) {
|
||||
bitsMessage = Messages.STATE;
|
||||
} else {
|
||||
bitsMessage |= Messages.STATE;
|
||||
printString: function(s) {
|
||||
if (DEBUG && DEBUGGER && this.dbg) {
|
||||
if (this.dbg.messageEnabled(Messages.STATE)) {
|
||||
this.dbg.message(s);
|
||||
}
|
||||
return this.dbg.messageEnabled(bitsMessage);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* printMessage(sMessage)
|
||||
*
|
||||
* @this {State}
|
||||
* @param {string} sMessage is any caller-defined message string
|
||||
*/
|
||||
printMessage: function(sMessage) {
|
||||
if (DEBUGGER && this.dbg) this.dbg.message(sMessage);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -218,6 +218,8 @@ if (PREFETCH) {
|
|||
X86CPU.PFINFO.IP_MASK = ((X86CPU.PFINFO.LENGTH - 1) & ~0x3);
|
||||
}
|
||||
|
||||
X86CPU.PAGEBLOCKS_CACHE = 512; // TODO: This seems adequate for 4Mb of RAM, but it should be dynamically reconfigured
|
||||
|
||||
/**
|
||||
* initMemory(aMemBlocks, nBlockShift)
|
||||
*
|
||||
|
|
@ -432,6 +434,12 @@ X86CPU.prototype.enablePageBlocks = function()
|
|||
* if the Debugger is suppressing faults or calling probeAddr(), returning memEmpty is helpful.
|
||||
*/
|
||||
this.memEmpty = new Memory();
|
||||
|
||||
/*
|
||||
* Initialize our PAGEBLOCKS cache (see acquirePageBlock() and releasePageBlock()).
|
||||
*/
|
||||
this.aCacheBlocks = new Array(X86CPU.PAGEBLOCKS_CACHE);
|
||||
this.iCacheBlocks = 0;
|
||||
} else {
|
||||
/*
|
||||
* Our equivalent of a TLB flush. NOTE: We do not attempt to simulate an actual TLB; our
|
||||
|
|
@ -440,12 +448,62 @@ X86CPU.prototype.enablePageBlocks = function()
|
|||
* a constrained TLB -- at least not from the 80386 era, which is all we're emulating.
|
||||
*/
|
||||
for (var i = 0; i < this.aBlocksPaged.length; i++) {
|
||||
this.aMemBlocks[this.aBlocksPaged[i]] = this.blockUnpaged;
|
||||
var iBlock = this.aBlocksPaged[i];
|
||||
this.releasePageBlock(this.aMemBlocks[iBlock]);
|
||||
this.aMemBlocks[iBlock] = this.blockUnpaged;
|
||||
}
|
||||
}
|
||||
this.aBlocksPaged = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* acquirePageBlock(addr)
|
||||
*
|
||||
* This implements a simple paged memory block cache. Candidates for caching must be released via
|
||||
* releasePageBlock().
|
||||
*
|
||||
* After acquiring a block from this cache, the caller MUST use setPhysBlock() to properly reinitialize
|
||||
* it for the new given linear address.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} addr
|
||||
* @return {Memory}
|
||||
*/
|
||||
X86CPU.prototype.acquirePageBlock = function(addr)
|
||||
{
|
||||
var block;
|
||||
if (this.iCacheBlocks > 0) {
|
||||
block = this.aCacheBlocks[--this.iCacheBlocks];
|
||||
/*
|
||||
* Paged memory blocks are all very generic and contain no memory of their own, so the fact
|
||||
* that we're not calling the Memory constructor to reinitialize it is OK. setPhysBlock() is
|
||||
* what's critical, and the caller will take care of that. However, to avoid any confusion,
|
||||
* especially when debugging, there are a few properties we should reinitialize, hence init().
|
||||
*/
|
||||
block.init(addr);
|
||||
} else {
|
||||
block = new Memory(addr, 0, 0, Memory.TYPE.PAGED);
|
||||
}
|
||||
return block;
|
||||
};
|
||||
|
||||
/**
|
||||
* releasePageBlock(block)
|
||||
*
|
||||
* Instead of simply tossing Memory blocks onto the garbage collector's heap, we'll retain a maximum
|
||||
* number (X86CPU.PAGEBLOCKS_CACHE) in aCacheBlocks, with iCacheBlocks pointing to the next free element.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {Memory} block
|
||||
*/
|
||||
X86CPU.prototype.releasePageBlock = function(block)
|
||||
{
|
||||
this.assert(block && block.type === Memory.TYPE.PAGED);
|
||||
if (this.iCacheBlocks < X86CPU.PAGEBLOCKS_CACHE) {
|
||||
this.aCacheBlocks[this.iCacheBlocks++] = block;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* mapPageBlock(addr, fWrite, fSuppress)
|
||||
*
|
||||
|
|
@ -535,14 +593,13 @@ X86CPU.prototype.mapPageBlock = function(addr, fWrite, fSuppress)
|
|||
*
|
||||
* Now we can 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, 0, Memory.TYPE.PAGED);
|
||||
var blockPage = this.acquirePageBlock(addr & ~X86.LADDR.OFFSET);
|
||||
blockPage.setPhysBlock(blockPhys, blockPDE, offPDE, blockPTE, offPTE);
|
||||
blockPage.copyBreakpoints(this.dbg, block);
|
||||
|
||||
this.aMemBlocks[iBlock] = blockPage;
|
||||
|
||||
this.aBlocksPaged.push(iBlock);
|
||||
|
||||
return blockPage;
|
||||
};
|
||||
|
||||
|
|
@ -563,6 +620,19 @@ X86CPU.prototype.disablePageBlocks = function()
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* isPagingEnabled()
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @return {boolean}
|
||||
*/
|
||||
X86CPU.prototype.isPagingEnabled = function()
|
||||
{
|
||||
var fPaging = !!(this.regCR0 & X86.CR0.PG);
|
||||
this.assert((this.aMemBlocks !== this.aBusBlocks) === fPaging);
|
||||
return fPaging;
|
||||
};
|
||||
|
||||
/**
|
||||
* initProcessor()
|
||||
*
|
||||
|
|
@ -1717,7 +1787,7 @@ X86CPU.prototype.save = function()
|
|||
state.set(1, a);
|
||||
state.set(2, [this.segData.sName, this.segStack.sName, this.opFlags, this.opPrefixes, this.intFlags, this.regEA, this.regEAWrite]);
|
||||
state.set(3, [0, this.nTotalCycles, this.getSpeed()]);
|
||||
state.set(4, this.bus.saveMemory());
|
||||
state.set(4, this.bus.saveMemory(this.isPagingEnabled()));
|
||||
return state.data();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4116,7 +4116,8 @@ X86.fnFaultMessage = function(nFault, nError, fHalt)
|
|||
var sMessage = "Fault " + str.toHexByte(nFault) + (nError != null? " (" + str.toHexWord(nError) + ")" : "") + " on opcode " + str.toHexByte(bOpcode);
|
||||
if (fHalt && fRunning) sMessage += " (blocked by PCjs Debugger)";
|
||||
|
||||
if (this.printMessage(sMessage, fHalt || bitsMessage, true)) {
|
||||
if (DEBUGGER && this.dbg) {
|
||||
this.printMessage(sMessage, fHalt || bitsMessage, true);
|
||||
if (fHalt) {
|
||||
/*
|
||||
* By setting fHalt to fRunning (which is true while running but false while single-stepping),
|
||||
|
|
@ -4131,8 +4132,8 @@ X86.fnFaultMessage = function(nFault, nError, fHalt)
|
|||
}
|
||||
} else {
|
||||
/*
|
||||
* If printMessage() returned false, then there's no Debugger, which means that messageEnabled() must have
|
||||
* returned false as well, which means that fHalt must be true. Which means we should shut the machine down.
|
||||
* If there's no Debugger, then messageEnabled() must have returned false, which means that fHalt must
|
||||
* be true. Which means we should shut the machine down.
|
||||
*/
|
||||
this.assert(fHalt);
|
||||
this.notice(sMessage);
|
||||
|
|
|
|||
|
|
@ -960,16 +960,13 @@ Component.prototype = {
|
|||
* @param {string} sMessage is any caller-defined message string
|
||||
* @param {number|boolean} [bitsMessage] is zero or more MESSAGE_* category flag(s)
|
||||
* @param {boolean} [fAddress] is true to display the current address
|
||||
* @return {boolean} true if Debugger available, false if not
|
||||
*/
|
||||
printMessage: function(sMessage, bitsMessage, fAddress) {
|
||||
if (DEBUGGER && this.dbg) {
|
||||
if (bitsMessage === true || this.messageEnabled(bitsMessage | 0)) {
|
||||
this.dbg.message(sMessage, fAddress);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* printMessageIO(port, bOut, addrFrom, name, bIn, bitsMessage)
|
||||
|
|
|
|||
Loading…
Reference in a new issue