Fixed Closure Compiler build (the compiler gets easily confused when objects in different class hierarchies have identical method names)

This commit is contained in:
Jeff Parsons 2015-11-08 21:38:15 -08:00
commit 39af7dbe32
6 changed files with 145 additions and 65 deletions

View file

@ -61,9 +61,10 @@ if (NODE) {
* addMemory(). If the component needs something more than simple read/write storage, * addMemory(). If the component needs something more than simple read/write storage,
* it must provide a controller with getMemoryBuffer() and getMemoryAccess() methods. * it must provide a controller with getMemoryBuffer() and getMemoryAccess() methods.
* *
* By contrast, all port (I/O) operations are defined by external handlers; they register * All port (I/O) operations are defined by external handlers; they register with us,
* with us, and we manage those registrations, as well as support for I/O breakpoints, * and we manage those registrations and provide support for I/O breakpoints, but the
* but unlike memory accesses, we're not involved with port data accesses. * only default I/O behavior we provide is ignoring writes to any unregistered output
* ports and returning 0xff from any unregistered input ports.
* *
* @constructor * @constructor
* @extends Component * @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, * 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 * 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. * helper methods compress() and decompress() to create and expand the compressed data arrays.
* *
* @this {Bus} * @this {Bus}
* @param {boolean} [fAll] (true to save all non-ROM memory blocks, regardless of their dirty flags)
* @return {Array} a * @return {Array} a
*/ */
Bus.prototype.saveMemory = function() Bus.prototype.saveMemory = function(fAll)
{ {
var i = 0; var i = 0;
var a = []; var a = [];
/* /*
* A quick-and-dirty work-around for 32-bit bus machines, to ensure that all blocks in the 2nd Mb are * 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 * mapped in before we save. We do this by forcing A20 on, and then turning it off again before we leave.
* leave.
*/ */
var fA20 = this.getA20(); var fA20 = this.getA20();
if (!fA20) this.setA20(true); 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, * 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. * 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++] = iBlock;
a[i++] = State.compress(block.save()); 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 * 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. * 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} * @this {Bus}
* @param {Array} a * @param {Array} a
@ -1466,12 +1467,11 @@ Bus.prototype.checkPortInputNotify = function(port, size, addrLIP)
var dataPort = maskPort; var dataPort = maskPort;
/* /*
* TODO: We need to decide what to do about 8-bit I/O to a 16-bit port * TODO: We need to decide what to do about 8-bit I/O to a 16-bit port (ditto for 16-bit I/O
* (ditto for 16-bit I/O to a 32-bit port). We probably should pass the * to a 32-bit port). We probably should pass the size through to the aNotify[0] handler,
* size through to the aNotify[0] handler, and let it decide what to do, * and let it decide what to do, but I don't feel like changing all the I/O handlers right now.
* but I don't feel like changing all the I/O handlers right now. The * The good news, at least, is that the 8-bit handlers would not have to do anything special.
* good news, at least, is that the 8-bit handlers would not have to do * This assert will warn us if this is a pressing need.
* anything special. This assert will warn us if this is a pressing need.
*/ */
this.assert(size >= sizePort); this.assert(size >= sizePort);
@ -1625,12 +1625,11 @@ Bus.prototype.checkPortOutputNotify = function(port, size, data, addrLIP)
var dataPort = (data >>>= shift) & maskPort; var dataPort = (data >>>= shift) & maskPort;
/* /*
* TODO: We need to decide what to do about 8-bit I/O to a 16-bit port * TODO: We need to decide what to do about 8-bit I/O to a 16-bit port (ditto for 16-bit I/O
* (ditto for 16-bit I/O to a 32-bit port). We probably should pass the * to a 32-bit port). We probably should pass the size through to the aNotify[0] handler,
* size through to the aNotify[0] handler, and let it decide what to do, * and let it decide what to do, but I don't feel like changing all the I/O handlers right now.
* but I don't feel like changing all the I/O handlers right now. The * The good news, at least, is that the 8-bit handlers would not have to do anything special.
* good news, at least, is that the 8-bit handlers would not have to do * This assert will warn us if this is a pressing need.
* anything special. This assert will warn us if this is a pressing need.
*/ */
this.assert(size >= sizePort); this.assert(size >= sizePort);

View file

@ -256,6 +256,17 @@ Memory.adjustEndian = function(dw) {
Memory.prototype = { Memory.prototype = {
constructor: Memory, constructor: Memory,
parent: null, 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) * clone(mem, type)
* *
@ -501,6 +512,7 @@ Memory.prototype = {
* @param {number} offPTE * @param {number} offPTE
*/ */
setPhysBlock: function(blockPhys, blockPDE, offPDE, blockPTE, offPTE) { setPhysBlock: function(blockPhys, blockPDE, offPDE, blockPTE, offPTE) {
this.blockPhys = blockPhys;
this.blockPDE = blockPDE; this.blockPDE = blockPDE;
this.iPDE = offPDE >> 2; // convert offPDE into iPDE (an adw index) this.iPDE = offPDE >> 2; // convert offPDE into iPDE (an adw index)
this.blockPTE = blockPTE; this.blockPTE = blockPTE;
@ -516,9 +528,9 @@ Memory.prototype = {
this.adw = blockPhys.adw; this.adw = blockPhys.adw;
this.setAccess(Memory.afnPagedLE); this.setAccess(Memory.afnPagedLE);
} else { } else {
this.blockPhys = blockPhys;
this.bitPTEAccessed = blockPhys? Memory.adjustEndian(X86.PTE.ACCESSED) : 0; this.bitPTEAccessed = blockPhys? Memory.adjustEndian(X86.PTE.ACCESSED) : 0;
this.bitPTEDirty = blockPhys? Memory.adjustEndian(X86.PTE.ACCESSED | X86.PTE.DIRTY) : 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.ab[off] = b;
this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED; this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED;
this.blockPTE.adw[this.iPTE] |= X86.PTE.ACCESSED | X86.PTE.DIRTY; 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) * writeShortBE(off, w, addr)
@ -1287,7 +1306,14 @@ Memory.prototype = {
} }
this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED; this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED;
this.blockPTE.adw[this.iPTE] |= X86.PTE.ACCESSED | X86.PTE.DIRTY; 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) * writeLongBE(off, l, addr)
@ -1347,7 +1373,14 @@ Memory.prototype = {
} }
this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED; this.blockPDE.adw[this.iPDE] |= X86.PTE.ACCESSED;
this.blockPTE.adw[this.iPTE] |= X86.PTE.ACCESSED | X86.PTE.DIRTY; 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) * readBackTrackNone(off)

View file

@ -278,9 +278,7 @@ State.prototype = {
if (s) { if (s) {
this[this.id] = s; this[this.id] = s;
this.fLoaded = true; this.fLoaded = true;
if (DEBUG && this.messageEnabled()) { if (DEBUG) this.printString("localStorage(" + this.key + "): " + s.length + " bytes loaded");
this.printMessage("localStorage(" + this.key + "): " + s.length + " bytes loaded");
}
return true; return true;
} }
} }
@ -317,9 +315,7 @@ State.prototype = {
if (web.hasLocalStorage()) { if (web.hasLocalStorage()) {
var s = JSON.stringify(this[this.id]); var s = JSON.stringify(this[this.id]);
if (web.setLocalStorageItem(this.key, s)) { if (web.setLocalStorageItem(this.key, s)) {
if (DEBUG && this.messageEnabled()) { if (DEBUG) this.printString("localStorage(" + this.key + "): " + s.length + " bytes stored");
this.printMessage("localStorage(" + this.key + "): " + s.length + " bytes stored");
}
} else { } else {
/* /*
* WARNING: Because browsers tend to disable all alerts() during an "unload" operation, * WARNING: Because browsers tend to disable all alerts() during an "unload" operation,
@ -377,40 +373,24 @@ State.prototype = {
var sKey = aKeys[i]; var sKey = aKeys[i];
if (sKey && (fAll || sKey.substr(0, this.key.length) == this.key)) { if (sKey && (fAll || sKey.substr(0, this.key.length) == this.key)) {
web.removeLocalStorageItem(sKey); web.removeLocalStorageItem(sKey);
if (DEBUG && this.messageEnabled()) { if (DEBUG) this.printString("localStorage(" + sKey + ") removed");
this.printMessage("localStorage(" + sKey + ") removed");
}
aKeys.splice(i, 1); aKeys.splice(i, 1);
i = 0; i = 0;
} }
} }
}, },
/** /**
* messageEnabled(bitsMessage) * printString(s)
* *
* @this {State} * @this {State}
* @param {number} [bitsMessage] is one or more Messages category flag(s) * @param {string} s is any caller-defined string
* @return {boolean}
*/ */
messageEnabled: function(bitsMessage) { printString: function(s) {
if (DEBUGGER && this.dbg) { if (DEBUG && DEBUGGER && this.dbg) {
if (bitsMessage == null) { if (this.dbg.messageEnabled(Messages.STATE)) {
bitsMessage = Messages.STATE; this.dbg.message(s);
} else {
bitsMessage |= Messages.STATE;
} }
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);
} }
}; };

View file

@ -218,6 +218,8 @@ if (PREFETCH) {
X86CPU.PFINFO.IP_MASK = ((X86CPU.PFINFO.LENGTH - 1) & ~0x3); 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) * initMemory(aMemBlocks, nBlockShift)
* *
@ -432,6 +434,12 @@ X86CPU.prototype.enablePageBlocks = function()
* if the Debugger is suppressing faults or calling probeAddr(), returning memEmpty is helpful. * if the Debugger is suppressing faults or calling probeAddr(), returning memEmpty is helpful.
*/ */
this.memEmpty = new Memory(); this.memEmpty = new Memory();
/*
* Initialize our PAGEBLOCKS cache (see acquirePageBlock() and releasePageBlock()).
*/
this.aCacheBlocks = new Array(X86CPU.PAGEBLOCKS_CACHE);
this.iCacheBlocks = 0;
} else { } else {
/* /*
* Our equivalent of a TLB flush. NOTE: We do not attempt to simulate an actual TLB; our * 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. * 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++) { 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 = []; 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) * 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(). * 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 = this.acquirePageBlock(addr & ~X86.LADDR.OFFSET);
var blockPage = new Memory(addrPage, 0, 0, Memory.TYPE.PAGED);
blockPage.setPhysBlock(blockPhys, blockPDE, offPDE, blockPTE, offPTE); blockPage.setPhysBlock(blockPhys, blockPDE, offPDE, blockPTE, offPTE);
blockPage.copyBreakpoints(this.dbg, block); blockPage.copyBreakpoints(this.dbg, block);
this.aMemBlocks[iBlock] = blockPage; this.aMemBlocks[iBlock] = blockPage;
this.aBlocksPaged.push(iBlock); this.aBlocksPaged.push(iBlock);
return blockPage; 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() * initProcessor()
* *
@ -1717,7 +1787,7 @@ X86CPU.prototype.save = function()
state.set(1, a); 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(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(3, [0, this.nTotalCycles, this.getSpeed()]);
state.set(4, this.bus.saveMemory()); state.set(4, this.bus.saveMemory(this.isPagingEnabled()));
return state.data(); return state.data();
}; };

View file

@ -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); 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 (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) { if (fHalt) {
/* /*
* By setting fHalt to fRunning (which is true while running but false while single-stepping), * 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 { } else {
/* /*
* If printMessage() returned false, then there's no Debugger, which means that messageEnabled() must have * If there's no Debugger, then messageEnabled() must have returned false, which means that fHalt must
* returned false as well, which means that fHalt must be true. Which means we should shut the machine down. * be true. Which means we should shut the machine down.
*/ */
this.assert(fHalt); this.assert(fHalt);
this.notice(sMessage); this.notice(sMessage);

View file

@ -960,16 +960,13 @@ Component.prototype = {
* @param {string} sMessage is any caller-defined message string * @param {string} sMessage is any caller-defined message string
* @param {number|boolean} [bitsMessage] is zero or more MESSAGE_* category flag(s) * @param {number|boolean} [bitsMessage] is zero or more MESSAGE_* category flag(s)
* @param {boolean} [fAddress] is true to display the current address * @param {boolean} [fAddress] is true to display the current address
* @return {boolean} true if Debugger available, false if not
*/ */
printMessage: function(sMessage, bitsMessage, fAddress) { printMessage: function(sMessage, bitsMessage, fAddress) {
if (DEBUGGER && this.dbg) { if (DEBUGGER && this.dbg) {
if (bitsMessage === true || this.messageEnabled(bitsMessage | 0)) { if (bitsMessage === true || this.messageEnabled(bitsMessage | 0)) {
this.dbg.message(sMessage, fAddress); this.dbg.message(sMessage, fAddress);
} }
return true;
} }
return false;
}, },
/** /**
* printMessageIO(port, bOut, addrFrom, name, bIn, bitsMessage) * printMessageIO(port, bOut, addrFrom, name, bIn, bitsMessage)