First change for v1.17.6: DeskPro 386-compatible A20 management
This commit is contained in:
parent
c684371852
commit
f2cb4dc18d
127 changed files with 993 additions and 1003 deletions
|
|
@ -465,18 +465,43 @@ Bus.prototype.scanMemory = function(stats, addr, size)
|
|||
*/
|
||||
Bus.prototype.getA20 = function()
|
||||
{
|
||||
return this.busLimit == this.busMask;
|
||||
return !this.aBlocks2Mb && this.busLimit == this.busMask;
|
||||
};
|
||||
|
||||
/**
|
||||
* setA20(fEnable)
|
||||
*
|
||||
* On 32-bit bus machines, I've adopted the approach that Compaq took with DeskPro 386 machines,
|
||||
* which is to map the 1st Mb to the 2nd Mb whenever A20 is disabled, rather than blindly masking
|
||||
* the A20 address bit from all addresses; this is what the DeskPro 386 ROM BIOS requires.
|
||||
*
|
||||
* For 24-bit bus machines, we take the same approach that most if not all 80286 systems took, which
|
||||
* is simply masking the A20 address bit.
|
||||
*
|
||||
* TODO: On machines with a 32-bit bus, look into whether we can eliminate address masking altogether,
|
||||
* which seems feasible, provided all incoming addresses are already pre-truncated to 32 bits. Also,
|
||||
* confirm that DeskPro 386 machines mapped the ENTIRE 1st Mb to the 2nd, and not simply the first 64Kb,
|
||||
* which is technically all that 8086 address wrap-around compatibility would require.
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {boolean} fEnable is true to enable A20 (default), false to disable
|
||||
*/
|
||||
Bus.prototype.setA20 = function(fEnable)
|
||||
{
|
||||
if (this.nBusWidth > 20) {
|
||||
if (this.nBusWidth == 32) {
|
||||
if (fEnable) {
|
||||
if (this.aBlocks2Mb) {
|
||||
this.setMemoryBlocks(0x100000, 0x100000, this.aBlocks2Mb);
|
||||
this.aBlocks2Mb = null;
|
||||
}
|
||||
} else {
|
||||
if (!this.aBlocks2Mb) {
|
||||
this.aBlocks2Mb = this.getMemoryBlocks(0x100000, 0x100000);
|
||||
this.setMemoryBlocks(0x100000, 0x100000, this.getMemoryBlocks(0x0, 0x100000));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this.nBusWidth > 20) {
|
||||
var addrMask = (this.busMask & ~0x100000) | (fEnable? 0x100000 : 0);
|
||||
if (addrMask != this.busMask) {
|
||||
this.busMask = addrMask;
|
||||
|
|
@ -579,6 +604,12 @@ Bus.prototype.getMemoryBlocks = function(addr, size)
|
|||
/**
|
||||
* setMemoryBlocks(addr, size, aBlocks, type)
|
||||
*
|
||||
* If no type is specified, then specified address range uses all the provided blocks as-is;
|
||||
* this form of setMemoryBlocks() is used for complete physical aliases.
|
||||
*
|
||||
* Otherwise, new blocks are allocated with the specified type; the underlying memory from the
|
||||
* provided blocks is still used, but the new blocks may have different access to that memory.
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} addr is the starting physical address
|
||||
* @param {number} size of the request, in bytes
|
||||
|
|
@ -590,13 +621,14 @@ Bus.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
|
|||
var i = 0;
|
||||
var iBlock = addr >>> this.blockShift;
|
||||
while (size > 0 && iBlock < this.aMemBlocks.length) {
|
||||
var block;
|
||||
if (type === undefined) {
|
||||
block = aBlocks[i++];
|
||||
} else {
|
||||
block = new Memory(addr);
|
||||
if (DEBUGGER) block.setDebugInfo(this.cpu, this.dbg, this.blockSize);
|
||||
block.clone(aBlocks[i++], type);
|
||||
var block = aBlocks[i++];
|
||||
this.assert(block);
|
||||
if (!block) break;
|
||||
if (type !== undefined) {
|
||||
var blockNew = new Memory(addr);
|
||||
if (DEBUGGER) blockNew.setDebugInfo(this.cpu, this.dbg, this.blockSize);
|
||||
blockNew.clone(block, type);
|
||||
block = blockNew;
|
||||
}
|
||||
this.aMemBlocks[iBlock++] = block;
|
||||
size -= this.blockSize;
|
||||
|
|
|
|||
|
|
@ -4542,7 +4542,7 @@ ChipSet.prototype.set8042OutPort = function(b)
|
|||
{
|
||||
this.b8042OutPort = b;
|
||||
|
||||
this.cpu.setA20(!!(b & ChipSet.KBC.OUTPORT.A20_ON));
|
||||
this.bus.setA20(!!(b & ChipSet.KBC.OUTPORT.A20_ON));
|
||||
|
||||
if (!(b & ChipSet.KBC.OUTPORT.NO_RESET)) {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ RAM.init = function()
|
|||
* 0x00FE0000-0x00FFFFFF. Note that only the top 128Kb of "ramCPQ" addressibility is affected; the
|
||||
* rest of that memory, ranging anywhere from 256Kb to 640Kb, remains addressible at its original
|
||||
* location. Compaq's CEMM and VDISK utilities were generally the only software able to access that
|
||||
* remaining memory.
|
||||
* remaining memory (what Compaq refers to as "Compaq Built-in Memory").
|
||||
*
|
||||
* @constructor
|
||||
* @param {RAM} ram
|
||||
|
|
@ -426,6 +426,12 @@ CompaqController.writeByte = function writeCompaqControllerByte(off, b)
|
|||
if (!controller.aBlocksDst) {
|
||||
controller.aBlocksDst = bus.getMemoryBlocks(CompaqController.MAP_DST, CompaqController.MAP_SIZE);
|
||||
}
|
||||
/*
|
||||
* You might think that the next three lines could ALSO be moved to the preceding IF,
|
||||
* but it's possible for the write-protection feature to be enabled/disabled separately
|
||||
* from the mapping feature. We could avoid executing this code as well by checking the
|
||||
* current read-write state, but this is an infrequent operation, so there's no point.
|
||||
*/
|
||||
var aBlocks = bus.getMemoryBlocks(CompaqController.MAP_SRC, CompaqController.MAP_SIZE);
|
||||
var type = (b & CompaqController.MAPPINGS.READWRITE)? Memory.TYPE.RAM : Memory.TYPE.ROM;
|
||||
bus.setMemoryBlocks(CompaqController.MAP_DST, CompaqController.MAP_SIZE, aBlocks, type);
|
||||
|
|
|
|||
|
|
@ -186,11 +186,6 @@ function X86CPU(parmsCPU)
|
|||
* so that if/when we call restore(), it will have something to fill in.
|
||||
*/
|
||||
this.resetRegs();
|
||||
|
||||
/*
|
||||
* Initially, the logical A20 state should be true, but from this point on, it's up to the machine to decide.
|
||||
*/
|
||||
this.fA20 = true;
|
||||
}
|
||||
|
||||
Component.subclass(X86CPU, CPU);
|
||||
|
|
@ -642,42 +637,10 @@ X86CPU.prototype.initMemory = function(aMemBlocks, blockShift, blockLimit, block
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* setA20(fEnable)
|
||||
*
|
||||
* setA20() used to ONLY be a Bus function, but we now route all setA20() calls through the CPU,
|
||||
* so that the CPU can maintain a logical A20 state (fA20), separate from the physical A20 state.
|
||||
*
|
||||
* In real-mode, all cpu.setA20() calls go straight to bus.setA20(), and we update the logical
|
||||
* A20 state (fA20); in protected-mode, we only update the logical A20 state (fA20).
|
||||
*
|
||||
* In addition, when transitioning from real-mode to protected-mode, we call bus.setA20(true), and
|
||||
* when transitioning back to real-mode, we call bus.setA20(fA20). See setProtMode() for details.
|
||||
*
|
||||
* This gives the CPU an unusual amount of control over the A20 line, but it protects us from "bad"
|
||||
* protected-mode code that fails to ensure A20 is enabled; I've run into code in the Compaq DeskPro
|
||||
* 386 ROM BIOS that fails without this work-around. This seems like a fairly safe hack, because
|
||||
* it's hard to imagine any real-world protected-mode code relying on A20 being off. However, that
|
||||
* doesn't change the fact that this hack should NOT be necessary.
|
||||
*
|
||||
* TODO: Figure out why the DeskPro 386 ROM BIOS misbehaves under emulation, and eliminate this hack.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {boolean} fEnable is true to enable A20, false to disable
|
||||
*/
|
||||
X86CPU.prototype.setA20 = function(fEnable)
|
||||
{
|
||||
this.fA20 = fEnable;
|
||||
if (!(this.regCR0 & X86.CR0.MSW.PE)) {
|
||||
this.bus.setA20(fEnable);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* setAddressMask(busMask)
|
||||
*
|
||||
* Notification from Bus.setA20(), called whenever the physical A20 line changes; this is
|
||||
* independent of the CPU's own logical A20 state (fA20).
|
||||
* Notification from Bus.setA20(), called whenever the physical A20 line changes
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} busMask
|
||||
|
|
@ -1441,16 +1404,6 @@ X86CPU.prototype.setProtMode = function(fProt)
|
|||
this.segFS.updateMode(fProt);
|
||||
this.segGS.updateMode(fProt);
|
||||
}
|
||||
/*
|
||||
* Work-around to update the A20 line whenever transitioning modes; see cpu.setA20() for details.
|
||||
*
|
||||
* Unfortunately, we can't immediately update the physical A20 line on return to real-mode, because
|
||||
* segment registers are likely still loaded with base addresses above 1Mb, so we leave the physical
|
||||
* A20 line enabled for now.
|
||||
*
|
||||
* if (this.bus) this.bus.setA20(fProt? true : this.fA20);
|
||||
*/
|
||||
if (this.bus && fProt) this.bus.setA20(true);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1464,7 +1417,7 @@ X86CPU.prototype.setProtMode = function(fProt)
|
|||
X86CPU.prototype.saveProtMode = function()
|
||||
{
|
||||
if (this.addrGDT != null) {
|
||||
return [this.regCR0, this.addrGDT, this.addrGDTLimit, this.addrIDT, this.addrIDTLimit, this.segLDT.save(), this.segTSS.save(), this.nIOPL, this.fA20];
|
||||
return [this.regCR0, this.addrGDT, this.addrGDTLimit, this.addrIDT, this.addrIDTLimit, this.segLDT.save(), this.segTSS.save(), this.nIOPL];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
|
@ -1488,7 +1441,6 @@ X86CPU.prototype.restoreProtMode = function(a)
|
|||
this.segLDT.restore(a[5]);
|
||||
this.segTSS.restore(a[6]);
|
||||
this.nIOPL = a[7];
|
||||
this.fA20 = (a[8] !== undefined? a[8] : this.bus.getA20());
|
||||
this.setProtMode();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue