Added Compaq DeskPro 386 ROMs
This commit is contained in:
parent
889a04b7d2
commit
eb196c3048
27 changed files with 5541 additions and 2285 deletions
|
|
@ -113,24 +113,31 @@ function Bus(parmsBus, cpu, dbg)
|
|||
* this.blockTotal Bus.BLOCK.TOTAL ((this.busLimit + this.blockSize) / this.blockSize) | 0
|
||||
* this.blockMask Bus.BLOCK.MASK (this.blockTotal - 1) (ie, 0xff)
|
||||
*
|
||||
* Note that the blockShift calculation below chooses a 4Kb physical memory block size for a 20-bit bus
|
||||
* (1Mb address space) and a 16Kb physical memory block for a 24-bit bus (16Mb address space). This yields
|
||||
* a 256-block array for the smaller bus and a 1024-block array for the larger bus. If we left the block
|
||||
* size at 4Kb in all cases, we'd end up with a 4096-block array for an 80286, which seems a bit excessive.
|
||||
* Note that we choose a blockShift value (and thus a physical memory block size) based on "buswidth":
|
||||
*
|
||||
* I can't think of any reason why a coarser block granularity (of 16Kb) should hurt anything, other than
|
||||
* wasting a little memory for ROMs smaller than the block size. Realize that this is strictly a physical
|
||||
* memory implementation detail, which should have no bearing on segment or page granularity of any future
|
||||
* virtual memory implementation.
|
||||
* Bus Width Block Shift Block Size
|
||||
* --------- ----------- ----------
|
||||
* 20 bits (1Mb address space): 12 4Kb (256 maximum blocks)
|
||||
* 24 bits (16Mb address space): 14 16Kb (1K maximum blocks)
|
||||
* 32 bits (4Gb address space); 15 32Kb (128K maximum blocks)
|
||||
*
|
||||
* The coarser block granularities (ie, 16Kb and 32Kb) may cause problems for certain RAM and/or ROM
|
||||
* allocations that are contiguous but are allocated out of order, or that have different controller
|
||||
* requirements. Your choices, for the moment, are either to ensure the allocations are performed in
|
||||
* order, or to choose smaller blockShift values (at the expense of a generating a larger block array).
|
||||
*
|
||||
* Be aware that this is strictly a physical memory implementation detail, which should have no bearing
|
||||
* on segment or page granularity of any future virtual memory implementation.
|
||||
*/
|
||||
this.busLimit = this.busMask = (1 << this.nBusWidth) - 1;
|
||||
this.blockShift = (this.nBusWidth <= 20? 12 : 14);
|
||||
this.addrTotal = Math.pow(2, this.nBusWidth);
|
||||
this.busLimit = this.busMask = (this.addrTotal - 1) | 0;
|
||||
this.blockShift = (this.nBusWidth <= 20? 12 : (this.nBusWidth <= 24? 14 : 15));
|
||||
this.blockSize = 1 << this.blockShift;
|
||||
this.blockLen = this.blockSize >> 2;
|
||||
this.blockLimit = this.blockSize - 1;
|
||||
this.blockTotal = ((this.busLimit + this.blockSize) / this.blockSize) | 0;
|
||||
this.blockTotal = (this.addrTotal / this.blockSize) | 0;
|
||||
this.blockMask = this.blockTotal - 1;
|
||||
this.assert(this.blockTotal <= Bus.BLOCK.NUM_MASK);
|
||||
this.assert(this.blockMask <= Bus.BLOCK.NUM_MASK);
|
||||
|
||||
/*
|
||||
* Lists of I/O notification functions: aPortInputNotify and aPortOutputNotify are arrays, indexed by
|
||||
|
|
@ -237,16 +244,16 @@ if (BACKTRACK) {
|
|||
}
|
||||
|
||||
/*
|
||||
* scanMemory() records block numbers in bits 0-14, a BackTrack "mod" bit in bit 15, and a block type at bit 28;
|
||||
* scanMemory() records block numbers in bits 0-16, a BackTrack "mod" bit in bit 17, and a block type at bit 28;
|
||||
* the bits reserved for a count are not used.
|
||||
*/
|
||||
Bus.BLOCK = {
|
||||
NUM_SHIFT: 0,
|
||||
NUM_MASK: 0x7fff,
|
||||
BTMOD_SHIFT: 15,
|
||||
NUM_MASK: 0x1ffff,
|
||||
BTMOD_SHIFT: 17,
|
||||
BTMOD_MASK: 0x1,
|
||||
COUNT_SHIFT: 16,
|
||||
COUNT_MASK: 0x0fff,
|
||||
COUNT_SHIFT: 18,
|
||||
COUNT_MASK: 0x03ff,
|
||||
TYPE_SHIFT: 28,
|
||||
TYPE_MASK: 0x7
|
||||
};
|
||||
|
|
@ -264,7 +271,7 @@ Bus.prototype.initMemory = function()
|
|||
for (var iBlock = 0; iBlock < this.blockTotal; iBlock++) {
|
||||
var addr = iBlock * this.blockSize;
|
||||
var block = this.aMemBlocks[iBlock] = new Memory(addr);
|
||||
if (DEBUGGER) block.setDebugInfo(this.cpu, this.dbg, addr, this.blockSize);
|
||||
if (DEBUGGER) block.setDebugInfo(this.cpu, this.dbg, this.blockSize);
|
||||
}
|
||||
this.cpu.initMemory(this.aMemBlocks, this.blockShift, this.blockLimit, this.blockMask);
|
||||
this.cpu.setAddressMask(this.busMask);
|
||||
|
|
@ -309,35 +316,73 @@ Bus.prototype.powerUp = function(data, fRepower)
|
|||
*
|
||||
* Adds new Memory blocks to the specified address range. Any Memory blocks previously
|
||||
* added to that range must first be removed via removeMemory(); otherwise, you'll get
|
||||
* an allocation conflict error. Moreover, the address range must start at a block-granular
|
||||
* address and span exactly one or more blocks; otherwise, you'll get a memory range error.
|
||||
* an allocation conflict error. This helps prevent address calculation errors, redundant
|
||||
* allocations, etc.
|
||||
*
|
||||
* These restrictions help prevent address calculation errors, redundant allocations, etc.
|
||||
* We've relaxed some of the original requirements (ie, that addresses must start at a
|
||||
* block-granular address, or that sizes must be equal to exactly one or more blocks), because
|
||||
* machines with large block sizes can make it impossible to load certain ROMs at at their
|
||||
* required addresses.
|
||||
*
|
||||
* Even so, Bus memory management does NOT provide a general-purpose heap. Most memory
|
||||
* allocations occur during machine initialization and never change. The only notable
|
||||
* exception is the Video frame buffer, which ranges from 4Kb (MDA) to 16Kb (CGA) to
|
||||
* 32Kb/64Kb/128Kb (EGA), and only the EGA changes the buffer address post-initialization.
|
||||
*
|
||||
* Each Memory block keeps track of a single address (addr) and length (used), indicating
|
||||
* the used space within the block; any free space that precedes or follows that used space
|
||||
* can be allocated later, by simply extending the beginning or ending of the previously used
|
||||
* space. However, any holes that might have existed between the original allocation and an
|
||||
* extension are subsumed by the extension.
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} addr is the starting physical address of the memory address range
|
||||
* @param {number} size of the length in bytes of the range; must be a multiple of blockSize
|
||||
* @param {number} addr is the starting physical address of the request
|
||||
* @param {number} size of the request, in bytes
|
||||
* @param {number} type is one of the Memory.TYPE constants
|
||||
* @param {Object} [controller] is an optional memory controller component
|
||||
* @return {boolean} true if successful, false if not
|
||||
*/
|
||||
Bus.prototype.addMemory = function(addr, size, type, controller)
|
||||
{
|
||||
if (!(addr & this.blockLimit) && size && !(size & this.blockLimit)) {
|
||||
var iBlock = addr >> this.blockShift;
|
||||
while (size > 0 && iBlock < this.aMemBlocks.length) {
|
||||
var block = this.aMemBlocks[iBlock];
|
||||
if (block !== undefined && block.size) {
|
||||
return this.reportError(1, addr, size);
|
||||
var iBlock = addr >>> this.blockShift;
|
||||
while (size > 0 && iBlock < this.aMemBlocks.length) {
|
||||
var block = this.aMemBlocks[iBlock];
|
||||
var addrBlock = iBlock * this.blockSize;
|
||||
var sizeBlock = size > this.blockSize? this.blockSize : size;
|
||||
if (block && block.size) {
|
||||
if (block.type == type && block.controller == controller) {
|
||||
/*
|
||||
* Where there is already a block with a non-zero size, we can allow the allocation only if:
|
||||
*
|
||||
* 1) addr + size <= block.addr (the request precedes the used portion of the current block)
|
||||
* or:
|
||||
* 2) addr >= block.addr + block.used (the request follows the used portion of the current block)
|
||||
*/
|
||||
if (addr + size <= block.addr) {
|
||||
block.used += (block.addr - addr);
|
||||
block.addr = addr;
|
||||
return true;
|
||||
}
|
||||
if (addr >= block.addr + block.used) {
|
||||
var sizeAvail = block.size - (addr - addrBlock);
|
||||
if (sizeAvail > size) sizeAvail = size;
|
||||
block.used = addr - block.addr + sizeAvail;
|
||||
size -= sizeAvail;
|
||||
addr = addrBlock + this.blockSize;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
addr = iBlock * this.blockSize;
|
||||
block = this.aMemBlocks[iBlock++] = new Memory(addr, this.blockSize, type, controller);
|
||||
if (DEBUGGER) block.setDebugInfo(this.cpu, this.dbg, addr, this.blockSize);
|
||||
size -= this.blockSize;
|
||||
return this.reportError(1, addr, size);
|
||||
}
|
||||
return true;
|
||||
block = this.aMemBlocks[iBlock++] = new Memory(addr, sizeBlock, this.blockSize, type, controller);
|
||||
if (DEBUGGER) block.setDebugInfo(this.cpu, this.dbg, this.blockSize);
|
||||
size -= sizeBlock;
|
||||
addr = addrBlock + this.blockSize;
|
||||
}
|
||||
return this.reportError(2, addr, size);
|
||||
if (size > 0) {
|
||||
return this.reportError(2, addr, size);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -351,7 +396,7 @@ Bus.prototype.addMemory = function(addr, size, type, controller)
|
|||
Bus.prototype.cleanMemory = function(addr, size)
|
||||
{
|
||||
var fClean = true;
|
||||
var iBlock = addr >> this.blockShift;
|
||||
var iBlock = addr >>> this.blockShift;
|
||||
while (size > 0 && iBlock < this.aMemBlocks.length) {
|
||||
if (this.aMemBlocks[iBlock].fDirty) {
|
||||
this.aMemBlocks[iBlock].fDirty = fClean = false;
|
||||
|
|
@ -384,7 +429,7 @@ Bus.prototype.cleanMemory = function(addr, size)
|
|||
Bus.prototype.scanMemory = function(stats, addr, size)
|
||||
{
|
||||
if (addr == null) addr = 0;
|
||||
if (size == null) size = (this.busLimit + 1) - addr;
|
||||
if (size == null) size = (this.addrTotal - addr) | 0;
|
||||
if (stats == null) stats = {cbTotal: 0, cBlocks: 0, aBlocks: new Array(this.blockTotal)};
|
||||
|
||||
var iBlock = addr >>> this.blockShift;
|
||||
|
|
@ -470,7 +515,7 @@ Bus.prototype.getWidth = function()
|
|||
Bus.prototype.setMemoryAccess = function(addr, size, afn)
|
||||
{
|
||||
if (!(addr & this.blockLimit) && size && !(size & this.blockLimit)) {
|
||||
var iBlock = addr >> this.blockShift;
|
||||
var iBlock = addr >>> this.blockShift;
|
||||
while (size > 0) {
|
||||
var block = this.aMemBlocks[iBlock];
|
||||
if (!block.controller) {
|
||||
|
|
@ -490,6 +535,8 @@ Bus.prototype.setMemoryAccess = function(addr, size, afn)
|
|||
*
|
||||
* Replaces every block in the specified address range with empty Memory blocks that will ignore all reads/writes.
|
||||
*
|
||||
* TODO: Update the removeMemory() interface to reflect the relaxed requirements of the addMemory() interface.
|
||||
*
|
||||
* @this {Bus}
|
||||
* @param {number} addr
|
||||
* @param {number} size
|
||||
|
|
@ -498,11 +545,11 @@ Bus.prototype.setMemoryAccess = function(addr, size, afn)
|
|||
Bus.prototype.removeMemory = function(addr, size)
|
||||
{
|
||||
if (!(addr & this.blockLimit) && size && !(size & this.blockLimit)) {
|
||||
var iBlock = addr >> this.blockShift;
|
||||
var iBlock = addr >>> this.blockShift;
|
||||
while (size > 0) {
|
||||
addr = iBlock * this.blockSize;
|
||||
var block = this.aMemBlocks[iBlock++] = new Memory(addr);
|
||||
if (DEBUGGER) block.setDebugInfo(this.cpu, this.dbg, addr, this.blockSize);
|
||||
if (DEBUGGER) block.setDebugInfo(this.cpu, this.dbg, this.blockSize);
|
||||
size -= this.blockSize;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -522,7 +569,7 @@ Bus.prototype.removeMemory = function(addr, size)
|
|||
*/
|
||||
Bus.prototype.getByte = function(addr)
|
||||
{
|
||||
return this.aMemBlocks[(addr & this.busMask) >> this.blockShift].readByte(addr & this.blockLimit);
|
||||
return this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readByte(addr & this.blockLimit);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -536,7 +583,7 @@ Bus.prototype.getByte = function(addr)
|
|||
*/
|
||||
Bus.prototype.getByteDirect = function(addr)
|
||||
{
|
||||
return this.aMemBlocks[(addr & this.busMask) >> this.blockShift].readByteDirect(addr & this.blockLimit);
|
||||
return this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readByteDirect(addr & this.blockLimit);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -553,7 +600,7 @@ Bus.prototype.getByteDirect = function(addr)
|
|||
Bus.prototype.getShort = function(addr)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >>> this.blockShift;
|
||||
if (off != this.blockLimit) {
|
||||
return this.aMemBlocks[iBlock].readShort(off);
|
||||
}
|
||||
|
|
@ -572,7 +619,7 @@ Bus.prototype.getShort = function(addr)
|
|||
Bus.prototype.getShortDirect = function(addr)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >>> this.blockShift;
|
||||
if (off != this.blockLimit) {
|
||||
return this.aMemBlocks[iBlock].readShortDirect(off);
|
||||
}
|
||||
|
|
@ -593,7 +640,7 @@ Bus.prototype.getShortDirect = function(addr)
|
|||
Bus.prototype.getLong = function(addr)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >>> this.blockShift;
|
||||
if (off < this.blockLimit - 2) {
|
||||
return this.aMemBlocks[iBlock].readLong(off);
|
||||
}
|
||||
|
|
@ -613,7 +660,7 @@ Bus.prototype.getLong = function(addr)
|
|||
Bus.prototype.getLongDirect = function(addr)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >>> this.blockShift;
|
||||
if (off < this.blockLimit - 2) {
|
||||
return this.aMemBlocks[iBlock].readLongDirect(off);
|
||||
}
|
||||
|
|
@ -633,7 +680,7 @@ Bus.prototype.getLongDirect = function(addr)
|
|||
*/
|
||||
Bus.prototype.setByte = function(addr, b)
|
||||
{
|
||||
this.aMemBlocks[(addr & this.busMask) >> this.blockShift].writeByte(addr & this.blockLimit, b & 0xff);
|
||||
this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].writeByte(addr & this.blockLimit, b & 0xff);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -648,7 +695,7 @@ Bus.prototype.setByte = function(addr, b)
|
|||
*/
|
||||
Bus.prototype.setByteDirect = function(addr, b)
|
||||
{
|
||||
this.aMemBlocks[(addr & this.busMask) >> this.blockShift].writeByteDirect(addr & this.blockLimit, b & 0xff);
|
||||
this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].writeByteDirect(addr & this.blockLimit, b & 0xff);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -665,7 +712,7 @@ Bus.prototype.setByteDirect = function(addr, b)
|
|||
Bus.prototype.setShort = function(addr, w)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >>> this.blockShift;
|
||||
if (off != this.blockLimit) {
|
||||
this.aMemBlocks[iBlock].writeShort(off, w & 0xffff);
|
||||
return;
|
||||
|
|
@ -687,7 +734,7 @@ Bus.prototype.setShort = function(addr, w)
|
|||
Bus.prototype.setShortDirect = function(addr, w)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >>> this.blockShift;
|
||||
if (off != this.blockLimit) {
|
||||
this.aMemBlocks[iBlock].writeShortDirect(off, w & 0xffff);
|
||||
return;
|
||||
|
|
@ -710,7 +757,7 @@ Bus.prototype.setShortDirect = function(addr, w)
|
|||
Bus.prototype.setLong = function(addr, l)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >>> this.blockShift;
|
||||
if (off < this.blockLimit - 2) {
|
||||
this.aMemBlocks[iBlock].writeLong(off, l);
|
||||
return;
|
||||
|
|
@ -737,7 +784,7 @@ Bus.prototype.setLong = function(addr, l)
|
|||
Bus.prototype.setLongDirect = function(addr, l)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >>> this.blockShift;
|
||||
if (off < this.blockLimit - 2) {
|
||||
this.aMemBlocks[iBlock].writeLongDirect(off, l);
|
||||
return;
|
||||
|
|
@ -857,7 +904,7 @@ Bus.prototype.writeBackTrackObject = function(addr, bto, off)
|
|||
Bus.prototype.readBackTrack = function(addr)
|
||||
{
|
||||
if (BACKTRACK) {
|
||||
return this.aMemBlocks[(addr & this.busMask) >> this.blockShift].readBackTrack(addr & this.blockLimit);
|
||||
return this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readBackTrack(addr & this.blockLimit);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
|
@ -873,7 +920,7 @@ Bus.prototype.writeBackTrack = function(addr, bti)
|
|||
{
|
||||
if (BACKTRACK) {
|
||||
var slot = bti >>> Bus.BACKTRACK.SLOT_SHIFT;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >>> this.blockShift;
|
||||
var btiPrev = this.aMemBlocks[iBlock].writeBackTrack(addr & this.blockLimit, bti);
|
||||
var slotPrev = btiPrev >>> Bus.BACKTRACK.SLOT_SHIFT;
|
||||
if (slot != slotPrev) {
|
||||
|
|
@ -975,7 +1022,7 @@ Bus.prototype.updateBackTrackCode = function(addr, bti)
|
|||
} else {
|
||||
return;
|
||||
}
|
||||
this.aMemBlocks[(addr & this.busMask) >> this.blockShift].writeBackTrack(addr & this.blockLimit, bti);
|
||||
this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].writeBackTrack(addr & this.blockLimit, bti);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1145,7 +1192,7 @@ Bus.prototype.restoreMemory = function(a)
|
|||
Bus.prototype.addMemBreak = function(addr, fWrite)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
var iBlock = addr >> this.blockShift;
|
||||
var iBlock = addr >>> this.blockShift;
|
||||
this.aMemBlocks[iBlock].addBreakpoint(addr & this.blockLimit, fWrite);
|
||||
}
|
||||
};
|
||||
|
|
@ -1160,7 +1207,7 @@ Bus.prototype.addMemBreak = function(addr, fWrite)
|
|||
Bus.prototype.removeMemBreak = function(addr, fWrite)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
var iBlock = addr >> this.blockShift;
|
||||
var iBlock = addr >>> this.blockShift;
|
||||
this.aMemBlocks[iBlock].removeBreakpoint(addr & this.blockLimit, fWrite);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -256,13 +256,13 @@ Component.subclass(Component, ChipSet);
|
|||
/*
|
||||
* Supported Models
|
||||
*
|
||||
* Unless otherwise noted, all BIOS references refer to the *original* BIOS released with each model
|
||||
* Unless otherwise noted, all BIOS references refer to the *original* BIOS released with each model.
|
||||
*/
|
||||
ChipSet.MODEL_5150 = 5150; // used in reference to the 1st 5150 BIOS, dated Apr 24, 1981
|
||||
ChipSet.MODEL_5160 = 5160; // used in reference to the 1st 5160 BIOS, dated Nov 8, 1982
|
||||
ChipSet.MODEL_5170 = 5170; // used in reference to the 1st 5170 BIOS, dated Jan 10, 1984
|
||||
/*
|
||||
* The following are fake model numbers, used only to document issues/features of note in later BIOS revisions
|
||||
* The following are fake model numbers, used only to document issues/features of note in later IBM PC AT BIOS revisions.
|
||||
*/
|
||||
ChipSet.MODEL_5170_REV2 = 5170.2; // used in reference to the 2nd 5170 BIOS, dated Jun 10, 1985
|
||||
ChipSet.MODEL_5170_REV3 = 5170.3; // used in reference to the 3rd 5170 BIOS, dated Nov 15, 1985
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ CPU.prototype.powerUp = function(data, fRepower)
|
|||
this.resetChecksum();
|
||||
}
|
||||
/*
|
||||
* Give the Debugger a chance to do/print something once we've powered up (TODO: Review the necessity of this)
|
||||
* Give the Debugger a chance to do/print something once we've powered up
|
||||
*/
|
||||
if (DEBUGGER && this.dbg) {
|
||||
this.dbg.init();
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.INS = {
|
||||
NONE: 0, AAA: 1, AAD: 2, AAM: 3, AAS: 4, ADC: 5, ADD: 6, AND: 7,
|
||||
ARPL: 8, ASIZE: 9, BOUND: 10, BSF: 11, BSR: 12, BT: 13, BTC: 14, BTR: 15,
|
||||
ARPL: 8, AS: 9, BOUND: 10, BSF: 11, BSR: 12, BT: 13, BTC: 14, BTR: 15,
|
||||
BTS: 16, CALL: 17, CBW: 18, CLC: 19, CLD: 20, CLI: 21, CLTS: 22, CMC: 23,
|
||||
CMP: 24, CMPSB: 25, CMPSW: 26, CS: 27, CWD: 28, DAA: 29, DAS: 30, DEC: 31,
|
||||
DIV: 32, DS: 33, ENTER: 34, ES: 35, ESC: 36, FADD: 37, FBLD: 38, FBSTP: 39,
|
||||
|
|
@ -266,7 +266,7 @@ if (DEBUGGER) {
|
|||
LFS: 104, LGDT: 105, LGS: 106, LIDT: 107, LLDT: 108, LMSW: 109, LOADALL:110, LOCK: 111,
|
||||
LODSB: 112, LODSW: 113, LOOP: 114, LOOPNZ: 115, LOOPZ: 116, LSL: 117, LSS: 118, LTR: 119,
|
||||
MOV: 120, MOVSB: 121, MOVSW: 122, MOVSX: 123, MOVZX: 124, MUL: 125, NEG: 126, NOP: 127,
|
||||
NOT: 128, OR: 129, OSIZE: 130, OUT: 131, OUTS: 132, POP: 133, POPA: 134, POPF: 135,
|
||||
NOT: 128, OR: 129, OS: 130, OUT: 131, OUTS: 132, POP: 133, POPA: 134, POPF: 135,
|
||||
PUSH: 136, PUSHA: 137, PUSHF: 138, RCL: 139, RCR: 140, REPNZ: 141, REPZ: 142, RET: 143,
|
||||
RETF: 144, ROL: 145, ROR: 146, SAHF: 147, SALC: 148, SAR: 149, SBB: 150, SCASB: 151,
|
||||
SCASW: 152, SETBE: 153, SETC: 154, SETG: 155, SETGE: 156, SETL: 157, SETLE: 158, SETNBE: 159,
|
||||
|
|
@ -686,8 +686,8 @@ if (DEBUGGER) {
|
|||
/* 0x63 */ [Debugger.INS.ARPL, Debugger.TYPE_MODRM | Debugger.TYPE_WORD | Debugger.TYPE_OUT, Debugger.TYPE_REG | Debugger.TYPE_WORD | Debugger.TYPE_IN],
|
||||
/* 0x64 */ [Debugger.INS.FS, Debugger.TYPE_NONE | Debugger.TYPE_386],
|
||||
/* 0x65 */ [Debugger.INS.GS, Debugger.TYPE_NONE | Debugger.TYPE_386],
|
||||
/* 0x66 */ [Debugger.INS.OSIZE, Debugger.TYPE_NONE | Debugger.TYPE_386],
|
||||
/* 0x67 */ [Debugger.INS.ASIZE, Debugger.TYPE_NONE | Debugger.TYPE_386],
|
||||
/* 0x66 */ [Debugger.INS.OS, Debugger.TYPE_NONE | Debugger.TYPE_386],
|
||||
/* 0x67 */ [Debugger.INS.AS, Debugger.TYPE_NONE | Debugger.TYPE_386],
|
||||
|
||||
/* 0x68 */ [Debugger.INS.PUSH, Debugger.TYPE_IMM | Debugger.TYPE_VWORD | Debugger.TYPE_IN | Debugger.TYPE_286],
|
||||
/* 0x69 */ [Debugger.INS.IMUL, Debugger.TYPE_REG | Debugger.TYPE_WORD | Debugger.TYPE_BOTH | Debugger.TYPE_286, Debugger.TYPE_MODRM | Debugger.TYPE_WORDIW | Debugger.TYPE_IN],
|
||||
|
|
@ -1763,6 +1763,7 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.init = function()
|
||||
{
|
||||
this.println("Type ? for list of debugger commands");
|
||||
this.updateStatus();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1878,7 +1879,7 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.stopCPU = function(s, fBlockFaults)
|
||||
{
|
||||
if (s) this.println(s);
|
||||
this.cpu.stopCPU(!fBlockFaults);
|
||||
if (this.cpu) this.cpu.stopCPU(!fBlockFaults);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1978,6 +1979,7 @@ if (DEBUGGER) {
|
|||
{
|
||||
this.historyInit();
|
||||
this.cInstructions = 0;
|
||||
this.sMessagePrev = null;
|
||||
this.nCycles = 0;
|
||||
this.aAddrNextCode = this.newAddr(this.cpu.getIP(), this.cpu.getCS());
|
||||
/*
|
||||
|
|
@ -2292,7 +2294,7 @@ if (DEBUGGER) {
|
|||
* @param {Array} aAddr
|
||||
* @param {boolean} [fWrite]
|
||||
* @param {number} [cb] is number of extra bytes to check (0 or 1)
|
||||
* @return {number} is the corresponding physical address, or -1 if there's an error
|
||||
* @return {number} is the corresponding physical address, or X86.ADDR_INVALID
|
||||
*/
|
||||
Debugger.prototype.getAddr = function(aAddr, fWrite, cb)
|
||||
{
|
||||
|
|
@ -2312,9 +2314,11 @@ if (DEBUGGER) {
|
|||
}
|
||||
}
|
||||
/*
|
||||
* Map addresses in the top 64Kb (at the top of the 16Mb range) to the top of the 1Mb range.
|
||||
* We used to map addresses at the top 64Kb of the first 16Mb to the top of the first 1Mb,
|
||||
* but post-80286, that's no longer appropriate.
|
||||
*
|
||||
* if ((addr & 0xFF0000) == 0xFF0000) addr &= 0x0FFFFF;
|
||||
*/
|
||||
if ((addr & 0xFF0000) == 0xFF0000) addr &= 0x0FFFFF;
|
||||
return addr;
|
||||
};
|
||||
|
||||
|
|
@ -2335,7 +2339,7 @@ if (DEBUGGER) {
|
|||
{
|
||||
var b = 0xff;
|
||||
var addr = this.getAddr(aAddr, false, 0);
|
||||
if (addr >= 0) {
|
||||
if (addr != X86.ADDR_INVALID) {
|
||||
b = this.bus.getByteDirect(addr);
|
||||
this.assert((b == (b & 0xff)), "invalid byte (" + b + ") at address: " + this.hexAddr(aAddr));
|
||||
if (inc !== undefined) this.incAddr(aAddr, inc);
|
||||
|
|
@ -2355,7 +2359,7 @@ if (DEBUGGER) {
|
|||
{
|
||||
var w = 0xffff;
|
||||
var addr = this.getAddr(aAddr, false, 1);
|
||||
if (addr >= 0) {
|
||||
if (addr != X86.ADDR_INVALID) {
|
||||
w = this.bus.getShortDirect(addr);
|
||||
this.assert((w == (w & 0xffff)), "invalid word (" + w + ") at address: " + this.hexAddr(aAddr));
|
||||
if (inc !== undefined) this.incAddr(aAddr, inc);
|
||||
|
|
@ -2378,7 +2382,7 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.setByte = function(aAddr, b, inc)
|
||||
{
|
||||
var addr = this.getAddr(aAddr, true, 0);
|
||||
if (addr >= 0) {
|
||||
if (addr != X86.ADDR_INVALID) {
|
||||
this.bus.setByteDirect(addr, b);
|
||||
if (inc !== undefined) this.incAddr(aAddr, inc);
|
||||
this.cpu.updateCPU();
|
||||
|
|
@ -2396,7 +2400,7 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.setShort = function(aAddr, w, inc)
|
||||
{
|
||||
var addr = this.getAddr(aAddr, true, 1);
|
||||
if (addr >= 0) {
|
||||
if (addr != X86.ADDR_INVALID) {
|
||||
this.bus.setShortDirect(addr, w);
|
||||
if (inc !== undefined) this.incAddr(aAddr, inc);
|
||||
this.cpu.updateCPU();
|
||||
|
|
@ -2705,7 +2709,7 @@ if (DEBUGGER) {
|
|||
var cOperands = 2;
|
||||
var sOperands = "";
|
||||
if (bOpcode >= X86.OPCODE.MOVSB && bOpcode <= X86.OPCODE.CMPSW || bOpcode >= X86.OPCODE.STOSB && bOpcode <= X86.OPCODE.SCASW) {
|
||||
cOperands = 0; // HACK to suppress display of operands for the string instructions
|
||||
cOperands = 0; // HACK to suppress display of operands for string instructions
|
||||
}
|
||||
|
||||
for (var iOperand = 1; iOperand <= cOperands; iOperand++) {
|
||||
|
|
@ -3032,7 +3036,7 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.getRegStr = function(fProt)
|
||||
{
|
||||
if (fProt === undefined) {
|
||||
fProt = !!(this.cpu.regMSW & X86.MSW.PE);
|
||||
fProt = !!(this.cpu.regCR0 & X86.CR0.MSW.PE);
|
||||
}
|
||||
var s = "AX=" + str.toHexWord(this.cpu.regEAX) +
|
||||
" BX=" + str.toHexWord(this.cpu.regEBX) +
|
||||
|
|
@ -3049,7 +3053,7 @@ if (DEBUGGER) {
|
|||
this.getFlagStr("S") + this.getFlagStr("Z") + this.getFlagStr("A") + this.getFlagStr("P") + this.getFlagStr("C") +
|
||||
" PS=" + str.toHexWord(this.cpu.getPS());
|
||||
if (fProt) {
|
||||
s += " MS=" + str.toHexWord(this.cpu.regMSW) + '\n' +
|
||||
s += " MS=" + str.toHexWord(this.cpu.regCR0) + '\n' +
|
||||
this.getDTRStr("LD", this.cpu.segLDT.sel, this.cpu.segLDT.base, this.cpu.segLDT.base + this.cpu.segLDT.limit) + ' ' +
|
||||
this.getDTRStr("GD", null, this.cpu.addrGDT, this.cpu.addrGDTLimit) + ' ' +
|
||||
this.getDTRStr("ID", null, this.cpu.addrIDT, this.cpu.addrIDTLimit) + " TR=" + str.toHexWord(this.cpu.segTSS.sel) +
|
||||
|
|
@ -3073,10 +3077,11 @@ if (DEBUGGER) {
|
|||
* convention for linear addresses and provide a different syntax (eg, "%%") physical memory references.
|
||||
*
|
||||
* Address evaluation and validation (eg, range checks) are no longer performed at this stage. That's
|
||||
* done later, by getAddr(), which returns a negative result (-1) for invalid segments, out-of-range offsets,
|
||||
* done later, by getAddr(), which returns X86.ADDR_INVALID for invalid segments, out-of-range offsets,
|
||||
* etc. The Debugger's low-level get/set memory functions verify all getAddr() results, but even if an
|
||||
* invalid address is passed through to the Bus memory interfaces, the address will simply be masked with
|
||||
* Bus.busLimit; in the case of -1, that will generally refer to the last byte of physical address space.
|
||||
* Bus.busLimit; in the case of X86.ADDR_INVALID, that will generally refer to the top of the physical
|
||||
* address space.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string|undefined} sAddr
|
||||
|
|
@ -4592,8 +4597,8 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.doUnassemble = function(sAddr, sAddrEnd, n)
|
||||
{
|
||||
var aAddr = this.parseAddr(sAddr, Debugger.ADDR_CODE);
|
||||
if (aAddr[0] == null)
|
||||
return;
|
||||
|
||||
if (aAddr[0] == null) return;
|
||||
|
||||
if (n === undefined) n = 1;
|
||||
var aAddrEnd = this.newAddr(0xffff, aAddr[1], this.bus.busLimit);
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ var littleEndian = (TYPEDARRAYS? (function() {
|
|||
})() : false);
|
||||
|
||||
/**
|
||||
* Memory(addr, size, type, controller)
|
||||
* Memory(addr, used, size, type, controller)
|
||||
*
|
||||
* The Bus component allocates Memory objects so that each has a memory buffer with a
|
||||
* block-granular starting address and an address range equal to bus.blockSize; however,
|
||||
|
|
@ -104,16 +104,19 @@ var littleEndian = (TYPEDARRAYS? (function() {
|
|||
* is available).
|
||||
*
|
||||
* @constructor
|
||||
* @param {number} addr of block (must be some multiple of bus.blockSize)
|
||||
* @param {number} addr of lowest used address in block
|
||||
* @param {number} [used] portion of block in bytes (0 for none); must be a multiple of 4
|
||||
* @param {number} [size] of block's buffer in bytes (0 for none); must be a multiple of 4
|
||||
* @param {number} [type] is one of the Memory.TYPE constants (default is Memory.TYPE.NONE)
|
||||
* @param {Object} [controller] is an optional memory controller component
|
||||
*/
|
||||
function Memory(addr, size, type, controller)
|
||||
function Memory(addr, used, size, type, controller)
|
||||
{
|
||||
var i;
|
||||
this.adw = null;
|
||||
this.offset = 0;
|
||||
this.addr = addr;
|
||||
this.used = used;
|
||||
this.size = size || 0;
|
||||
this.type = type || Memory.TYPE.NONE;
|
||||
this.fReadOnly = (type == Memory.TYPE.ROM);
|
||||
|
|
@ -172,7 +175,7 @@ function Memory(addr, size, type, controller)
|
|||
/*
|
||||
* If littleEndian is true, we can use ab[], aw[] and adw[] directly; well, we can use them
|
||||
* whenever the offset is a multiple of 1, 2 or 4, respectively. Otherwise, we must fallback to
|
||||
* dv.getUint8()/dv.setUint8(), dv.getUint16()/dv.setUint16() and db.getInt32()/dv.setInt32().
|
||||
* dv.getUint8()/dv.setUint8(), dv.getUint16()/dv.setUint16() and dv.getInt32()/dv.setInt32().
|
||||
*/
|
||||
this.ab = new Uint8Array(this.buffer, 0, size);
|
||||
this.aw = new Uint16Array(this.buffer, 0, size >> 1);
|
||||
|
|
@ -937,21 +940,19 @@ Memory.prototype = {
|
|||
this.writeLong = this.fReadOnly? Memory.writeNone : this.writeLongDirect;
|
||||
},
|
||||
/**
|
||||
* setDebugInfo(cpu, dbg, addr, size)
|
||||
* setDebugInfo(cpu, dbg, size)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {X86CPU|Component} cpu
|
||||
* @param {Debugger|Component} dbg
|
||||
* @param {number} addr of block
|
||||
* @param {number} size of block
|
||||
*/
|
||||
setDebugInfo: function(cpu, dbg, addr, size) {
|
||||
setDebugInfo: function(cpu, dbg, size) {
|
||||
if (DEBUGGER) {
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.addr = addr;
|
||||
this.cReadBreakpoints = this.cWriteBreakpoints = 0;
|
||||
if (this.dbg) this.dbg.redoBreakpoints(addr, size);
|
||||
if (this.dbg) this.dbg.redoBreakpoints(this.addr, size);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -79,15 +79,19 @@ var X86 = {
|
|||
NT: 0x4000, // bit 14: Nested Task flag (always set on 8086/80186, clear on 80286 reset)
|
||||
BIT15: 0x8000 // bit 15: reserved (always set on 8086/80186, clear otherwise)
|
||||
},
|
||||
/*
|
||||
* Machine Status Word definitions (stored in regMSW)
|
||||
*/
|
||||
MSW: {
|
||||
PE: 0x0001, // protected-mode enabled
|
||||
MP: 0x0002, // monitor processor extension (ie, coprocessor)
|
||||
EM: 0x0004, // emulate processor extension
|
||||
TS: 0x0008, // task switch indicator
|
||||
SET: 0xfff0 // on the 80286, these are always set (TODO: Verify)
|
||||
CR0: {
|
||||
/*
|
||||
* Machine Status Word (MSW) bit definitions
|
||||
*/
|
||||
MSW: {
|
||||
PE: 0x0001, // protected-mode enabled
|
||||
MP: 0x0002, // monitor processor extension (ie, coprocessor)
|
||||
EM: 0x0004, // emulate processor extension
|
||||
TS: 0x0008, // task switch indicator
|
||||
ON: 0xfff0 // on the 80286, these bits are always on (TODO: Verify)
|
||||
},
|
||||
ET: 0x00000010, // coprocessor type (80287 or 80387); always 1 on post-80386 CPUs
|
||||
PG: 0x80000000|0 // 0: paging disabled
|
||||
},
|
||||
SEL: {
|
||||
RPL: 0x0003, // requested privilege level (0-3)
|
||||
|
|
|
|||
|
|
@ -101,8 +101,8 @@ function X86CPU(parmsCPU) {
|
|||
|
||||
var nCyclesDefault = 0;
|
||||
switch(this.model) {
|
||||
default:
|
||||
case X86.MODEL_8088:
|
||||
default:
|
||||
nCyclesDefault = 4772727;
|
||||
break;
|
||||
case X86.MODEL_80286:
|
||||
|
|
@ -701,6 +701,9 @@ X86CPU.prototype.initProcessor = function()
|
|||
this.OPFLAG_NOINTR8086 = X86.OPFLAG.NOINTR;
|
||||
this.nShiftCountMask = 0xff; // on an 8086/8088, all shift counts are used as-is
|
||||
|
||||
/*
|
||||
* TODO: Create an 80386-specific CYCLES table.
|
||||
*/
|
||||
this.CYCLES = (this.model >= X86.MODEL_80286? X86CPU.CYCLES_80286 : X86CPU.CYCLES_8088);
|
||||
|
||||
this.aOps = X86OpXX.aOps.slice(); // make copies of aOps and others before modifying them
|
||||
|
|
@ -818,12 +821,27 @@ X86CPU.prototype.resetRegs = function()
|
|||
this.regESI = 0;
|
||||
this.regEDI = 0;
|
||||
|
||||
/*
|
||||
* The following are internal "registers" that are used to capture intermediate values inside selected helper
|
||||
* functions and use them if they've been modified (or are known to always change); for example, the MUL and DIV
|
||||
* instructions perform calculations that must be propagated to specific registers (eg, AX and/or DX), which
|
||||
* the ModRM decoder functions don't know about. We initialize them here mainly for documentation purposes.
|
||||
*/
|
||||
this.regMD16 = this.regMD32 = -1;
|
||||
|
||||
/*
|
||||
* Another internal "register" we occasionally need is an interim copy of bModRM, set inside selected opcode
|
||||
* handlers so that the helper function can have access to the instruction's bModRM without resorting to a closure
|
||||
* (which, in the Chrome V8 engine, for example, seems to cause constant recompilation).
|
||||
*/
|
||||
this.bModRM = 0;
|
||||
|
||||
/*
|
||||
* NOTE: Even though the MSW and IDTR are 80286-specific, we initialize them for ALL CPUs, so that
|
||||
* functions like X86Help.opHelpINT() can use the same code for both. The 8086/8088 have no direct way
|
||||
* of accessing or changing them, so this internal change should be perfectly safe for those processors.
|
||||
*/
|
||||
this.regMSW = X86.MSW.SET;
|
||||
this.regCR0 = X86.CR0.MSW.ON;
|
||||
this.addrIDT = 0; this.addrIDTLimit = 0x03FF;
|
||||
this.nIOPL = 0; // this should be set before the first setPS() call
|
||||
|
||||
|
|
@ -844,14 +862,62 @@ X86CPU.prototype.resetRegs = function()
|
|||
this.segES = new X86Seg(this, X86Seg.ID.DATA, "ES");
|
||||
this.segSS = new X86Seg(this, X86Seg.ID.STACK, "SS");
|
||||
this.setSP(0);
|
||||
this.setSS(0);
|
||||
|
||||
if (I386 && this.model >= X86.MODEL_80386) {
|
||||
this.regCR0 = X86.CR0.ET;
|
||||
this.segFS = new X86Seg(this, X86Seg.ID.DATA, "FS");
|
||||
this.segGS = new X86Seg(this, X86Seg.ID.DATA, "GS");
|
||||
}
|
||||
|
||||
this.segNULL = new X86Seg(this, X86Seg.ID.NULL, "NULL");
|
||||
this.setCSIP(0, 0xFFFF); // this should be called before the first setPS() call
|
||||
|
||||
/*
|
||||
* The next few initializations mirror what we must do prior to each instruction (ie, inside the stepCPU() function);
|
||||
* note that opPrefixes, along with segData and segStack, are reset only after we've executed a non-prefix instruction.
|
||||
*/
|
||||
this.segData = this.segDS;
|
||||
this.segStack = this.segSS;
|
||||
this.opFlags = this.opPrefixes = 0;
|
||||
this.regEA = this.regEAWrite = X86.ADDR_INVALID;
|
||||
|
||||
/*
|
||||
* intFlags contains some internal states we use to indicate whether a hardware interrupt (INTFLAG.INTR) or
|
||||
* Trap software interrupt (INTR.TRAP) has been requested, as well as when we're in a "HLT" state (INTFLAG.HALT)
|
||||
* that requires us to wait for a hardware interrupt (INTFLAG.INTR) before continuing execution.
|
||||
*
|
||||
* intFlags must be cleared only by checkINTR(), whereas opFlags must be cleared prior to every CPU operation.
|
||||
*/
|
||||
this.intFlags = X86.INTFLAG.NONE;
|
||||
|
||||
/*
|
||||
* The following contain the (default) OPERAND size (2 for 16 bits, 4 for 32 bits), and the corresponding masks
|
||||
* for isolating the (src) bits of an OPERAND and clearing the (dst) bits of an OPERAND. These are reset to
|
||||
* their segCS counterparts at the start of every new instruction, but are also set here for documentation purposes.
|
||||
*/
|
||||
this.dataSize = this.segCS.dataSize;
|
||||
this.dataMask = this.segCS.dataMask;
|
||||
|
||||
/*
|
||||
* Similarly, the following contain the (default) ADDRESS size (2 for 16 bits, 4 for 32 bits), and the corresponding
|
||||
* masks for isolating the (src) bits of an address and clearing the (dst) bits of an address. Like the OPERAND size
|
||||
* properties, these are reset to their segCS counterparts at the start of every new instruction.
|
||||
*/
|
||||
this.addrSize = this.segCS.addrSize;
|
||||
this.addrMask = this.segCS.addrMask;
|
||||
|
||||
/*
|
||||
* It's also worth noting that instructions that implicitly use the stack also rely on something called STACK size,
|
||||
* which is based on the BIG bit of the last descriptor loaded into SS; use the following segSS properties:
|
||||
*
|
||||
* segSS.addrSize (2 or 4)
|
||||
* segSS.addrMask (0xffff or 0xffffffff)
|
||||
*
|
||||
* As there is no STACK size instruction prefix override, there's no need to propagate these segSS properties
|
||||
* to separate X86CPU properties, as we do for the OPERAND size and ADDRESS size properties.
|
||||
*/
|
||||
|
||||
this.setCSIP(0, 0xffff); // this should be called before the first setPS() call
|
||||
|
||||
if (BACKTRACK) {
|
||||
/*
|
||||
|
|
@ -907,12 +973,12 @@ X86CPU.prototype.resetRegs = function()
|
|||
/*
|
||||
* TODO: Verify what the 80286 actually sets addrGDT and addrGDTLimit to on reset (or if it leaves them alone).
|
||||
*/
|
||||
this.addrGDT = 0; this.addrGDTLimit = 0xFFFF; // GDTR
|
||||
this.addrGDT = 0; this.addrGDTLimit = 0xffff; // GDTR
|
||||
this.segLDT = new X86Seg(this, X86Seg.ID.LDT, "LDT", true); // LDTR
|
||||
this.segTSS = new X86Seg(this, X86Seg.ID.TSS, "TSS", true); // TR
|
||||
this.segVER = new X86Seg(this, X86Seg.ID.OTHER, "VER", true); // a scratch segment register for VERR and VERW instructions
|
||||
this.setCSIP(0xFFF0, 0xF000); // in real-mode, 0xF000 defaults the CS base address to 0x0F0000
|
||||
this.setCSBase(0xFF0000); // which is why we must manually adjust the CS base address to 0xFF0000
|
||||
this.setCSIP(0xfff0, 0xf000); // on an 80286 or 80386, the default CS:IP is 0xF000:0xFFF0 instead of 0xFFFF:0x0000
|
||||
this.setCSBase(0xffff0000|0); // on an 80286 or 80386, all CS base address bits above bit 15 must be set
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -926,66 +992,6 @@ X86CPU.prototype.resetRegs = function()
|
|||
*/
|
||||
this.setProtMode();
|
||||
|
||||
/*
|
||||
* intFlags contains some internal states we use to indicate whether a hardware interrupt (INTFLAG.INTR) or
|
||||
* Trap software interrupt (INTR.TRAP) has been requested, as well as when we're in a "HLT" state (INTFLAG.HALT)
|
||||
* that requires us to wait for a hardware interrupt (INTFLAG.INTR) before continuing execution.
|
||||
*
|
||||
* intFlags must be cleared only by checkINTR(), whereas opFlags must be cleared prior to every CPU operation.
|
||||
*/
|
||||
this.intFlags = X86.INTFLAG.NONE;
|
||||
|
||||
/*
|
||||
* The following are internal "registers" that are used to capture intermediate values inside selected helper
|
||||
* functions and use them if they've been modified (or are known to always change); for example, the MUL and DIV
|
||||
* instructions perform calculations that must be propagated to specific registers (eg, AX and/or DX), which
|
||||
* the ModRM decoder functions don't know about. We initialize them here mainly for documentation purposes.
|
||||
*/
|
||||
this.regMD16 = this.regMD32 = -1;
|
||||
|
||||
/*
|
||||
* Another internal "register" we occasionally need is an interim copy of bModRM, set inside selected opcode
|
||||
* handlers so that the helper function can have access to the instruction's bModRM without resorting to a closure
|
||||
* (which, in the Chrome V8 engine, for example, seems to cause constant recompilation).
|
||||
*/
|
||||
this.bModRM = 0;
|
||||
|
||||
/*
|
||||
* The next few initializations mirror what we must do prior to each instruction (ie, inside the stepCPU() function);
|
||||
* note that opPrefixes, along with segData and segStack, are reset only after we've executed a non-prefix instruction.
|
||||
*/
|
||||
this.regEA = this.regEAWrite = X86.ADDR_INVALID;
|
||||
this.segData = this.segDS;
|
||||
this.segStack = this.segSS;
|
||||
this.opFlags = this.opPrefixes = 0;
|
||||
|
||||
/*
|
||||
* The following contain the (default) OPERAND size (2 for 16 bits, 4 for 32 bits), and the corresponding masks
|
||||
* for isolating the (src) bits of an OPERAND and clearing the (dst) bits of an OPERAND. These are reset to
|
||||
* their segCS counterparts at the start of every new instruction, but are also set here for documentation purposes.
|
||||
*/
|
||||
this.dataSize = this.segCS.dataSize;
|
||||
this.dataMask = this.segCS.dataMask;
|
||||
|
||||
/*
|
||||
* Similarly, the following contain the (default) ADDRESS size (2 for 16 bits, 4 for 32 bits), and the corresponding
|
||||
* masks for isolating the (src) bits of an address and clearing the (dst) bits of an address. Like the OPERAND size
|
||||
* properties, these are reset to their segCS counterparts at the start of every new instruction.
|
||||
*/
|
||||
this.addrSize = this.segCS.addrSize;
|
||||
this.addrMask = this.segCS.addrMask;
|
||||
|
||||
/*
|
||||
* It's also worth noting that instructions that implicitly use the stack also rely on something called STACK size,
|
||||
* which is based on the BIG bit of the last descriptor loaded into SS; use the following segSS properties:
|
||||
*
|
||||
* segSS.addrSize (2 or 4)
|
||||
* segSS.addrMask (0xffff or 0xffffffff)
|
||||
*
|
||||
* As there is no STACK size instruction prefix override, there's no need to propagate these segSS properties
|
||||
* to separate X86CPU properties, as we do for the OPERAND size and ADDRESS size properties.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The memory dispatch tables; opMem refers to the active set, based on the current OPERAND size (dataSize),
|
||||
* which is based foremost on segCS.dataSize, but can also be overridden by an OPERAND size instruction prefix.
|
||||
|
|
@ -1176,7 +1182,7 @@ X86CPU.prototype.checkIntReturn = function(addr)
|
|||
X86CPU.prototype.setProtMode = function(fProt)
|
||||
{
|
||||
if (fProt === undefined) {
|
||||
fProt = !!(this.regMSW & X86.MSW.PE);
|
||||
fProt = !!(this.regCR0 & X86.CR0.MSW.PE);
|
||||
}
|
||||
if (!fProt) {
|
||||
this.printMessage("returning to real-mode");
|
||||
|
|
@ -1199,7 +1205,7 @@ X86CPU.prototype.setProtMode = function(fProt)
|
|||
X86CPU.prototype.saveProtMode = function()
|
||||
{
|
||||
if (this.addrGDT != null) {
|
||||
return [this.regMSW, this.addrGDT, this.addrGDTLimit, this.addrIDT, this.addrIDTLimit, this.segLDT.save(), this.segTSS.save(), this.nIOPL];
|
||||
return [this.regCR0, this.addrGDT, this.addrGDTLimit, this.addrIDT, this.addrIDTLimit, this.segLDT.save(), this.segTSS.save(), this.nIOPL];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
|
@ -1215,7 +1221,7 @@ X86CPU.prototype.saveProtMode = function()
|
|||
X86CPU.prototype.restoreProtMode = function(a)
|
||||
{
|
||||
if (a && a.length) {
|
||||
this.regMSW = a[0];
|
||||
this.regCR0 = a[0];
|
||||
this.addrGDT = a[1];
|
||||
this.addrGDTLimit = a[2];
|
||||
this.addrIDT = a[3];
|
||||
|
|
@ -1278,11 +1284,16 @@ X86CPU.prototype.restore = function(data)
|
|||
this.restoreProtMode(a[5]);
|
||||
this.setPS(a[6]);
|
||||
/*
|
||||
* Since we're not using setCS() and setSS(), it's important to call setIP() and setSP() *after* the segCS
|
||||
* and segSS loads, so that the CPU's linear IP and SP registers (regLIP and regLSP) will be updated properly.
|
||||
* Since we're not using setCS(), it's important to call setIP() *after* segCS is restored, so that the
|
||||
* CPU's linear IP register (regLIP) will be updated properly.
|
||||
*/
|
||||
this.setIP(a[0]);
|
||||
/*
|
||||
* It's also important to call setSP(), so that the linear SP register (regLSP) will be updated properly;
|
||||
* we also need to call setSS(), to ensure that the lower and upper stack limits are properly initialized.
|
||||
*/
|
||||
this.setSP(regESP);
|
||||
this.setSS(this.segSS.sel);
|
||||
if (I386 && this.model >= X86.MODEL_80386) {
|
||||
this.segFS.restore(a[7]);
|
||||
this.segGS.restore(a[8]);
|
||||
|
|
@ -1492,7 +1503,7 @@ X86CPU.prototype.setIP = function(off)
|
|||
*/
|
||||
X86CPU.prototype.setCSIP = function(off, sel, fCall)
|
||||
{
|
||||
this.assert(!this.addrMask || (off & this.addrMask) == off);
|
||||
this.assert((off & this.addrMask) == off);
|
||||
this.segCS.fCall = fCall;
|
||||
/*
|
||||
* We break this operation into the following discrete steps (eg, set IP, load CS, and then update IP) so
|
||||
|
|
@ -1523,7 +1534,7 @@ X86CPU.prototype.setCSIP = function(off, sel, fCall)
|
|||
X86CPU.prototype.setCSBase = function(addr)
|
||||
{
|
||||
var regIP = this.getIP();
|
||||
this.segCS.setBase(addr);
|
||||
addr = this.segCS.setBase(addr);
|
||||
this.regLIP = addr + regIP;
|
||||
this.regLIPLimit = addr + this.segCS.limit;
|
||||
};
|
||||
|
|
@ -1900,7 +1911,7 @@ X86CPU.prototype.setPS = function(regPS, cpl)
|
|||
* This has the added benefit of relieving us from zeroing the effective IOPL (this.nIOPL) whenever
|
||||
* we're in real-mode, since we're zeroing the incoming IOPL bits up front now.
|
||||
*/
|
||||
if (!(this.regMSW & X86.MSW.PE)) {
|
||||
if (!(this.regCR0 & X86.CR0.MSW.PE)) {
|
||||
regPS &= ~(X86.PS.IOPL.MASK | X86.PS.NT | X86.PS.BIT15);
|
||||
}
|
||||
|
||||
|
|
@ -2015,7 +2026,7 @@ X86CPU.prototype.setBinding = function(sHTMLType, sBinding, control)
|
|||
X86CPU.prototype.getByte = function(addr)
|
||||
{
|
||||
if (BACKTRACK) this.backTrack.btiMemLo = this.bus.readBackTrack(addr);
|
||||
return this.aMemBlocks[(addr & this.busMask) >> this.blockShift].readByte(addr & this.blockLimit);
|
||||
return this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readByte(addr & this.blockLimit);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2028,7 +2039,7 @@ X86CPU.prototype.getByte = function(addr)
|
|||
X86CPU.prototype.getShort = function(addr)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >>> this.blockShift;
|
||||
/*
|
||||
* On the 8088, it takes 4 cycles to read the additional byte REGARDLESS whether the address is odd or even.
|
||||
* TODO: For the 8086, the penalty is actually "(addr & 0x1) << 2" (4 additional cycles only when the address is odd).
|
||||
|
|
@ -2055,7 +2066,7 @@ X86CPU.prototype.getShort = function(addr)
|
|||
X86CPU.prototype.getLong = function(addr)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >>> this.blockShift;
|
||||
if (BACKTRACK) {
|
||||
this.backTrack.btiMemLo = this.bus.readBackTrack(addr);
|
||||
this.backTrack.btiMemHi = this.bus.readBackTrack(addr + 1);
|
||||
|
|
@ -2077,7 +2088,7 @@ X86CPU.prototype.getLong = function(addr)
|
|||
X86CPU.prototype.setByte = function(addr, b)
|
||||
{
|
||||
if (BACKTRACK) this.bus.writeBackTrack(addr, this.backTrack.btiMemLo);
|
||||
this.aMemBlocks[(addr & this.busMask) >> this.blockShift].writeByte(addr & this.blockLimit, b & 0xff);
|
||||
this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].writeByte(addr & this.blockLimit, b & 0xff);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2090,7 +2101,7 @@ X86CPU.prototype.setByte = function(addr, b)
|
|||
X86CPU.prototype.setShort = function(addr, w)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >>> this.blockShift;
|
||||
/*
|
||||
* On the 8088, it takes 4 cycles to write the additional byte REGARDLESS whether the address is odd or even.
|
||||
* TODO: For the 8086, the penalty is actually "(addr & 0x1) << 2" (4 additional cycles only when the address is odd).
|
||||
|
|
@ -2119,7 +2130,7 @@ X86CPU.prototype.setShort = function(addr, w)
|
|||
X86CPU.prototype.setLong = function(addr, l)
|
||||
{
|
||||
var off = addr & this.blockLimit;
|
||||
var iBlock = (addr & this.busMask) >> this.blockShift;
|
||||
var iBlock = (addr & this.busMask) >>> this.blockShift;
|
||||
this.nStepCycles -= this.CYCLES.nWordCyclePenalty;
|
||||
|
||||
if (BACKTRACK) {
|
||||
|
|
@ -2440,7 +2451,7 @@ X86CPU.prototype.getBytePrefetch = function(addr)
|
|||
* with side-effects we may not want, and in any case, while it seemed to improve Safari's performance slightly,
|
||||
* it did nothing for the oddball Chrome performance I'm seeing with PREFETCH enabled.
|
||||
*
|
||||
* b = this.aMemBlocks[(addr & this.busMask) >> this.blockShift].readByte(addr & this.blockLimit);
|
||||
* b = this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readByte(addr & this.blockLimit);
|
||||
* this.nBusCycles += 4;
|
||||
* this.cbPrefetchValid = 0;
|
||||
* this.addrPrefetchHead = (addr + 1) & this.busMask;
|
||||
|
|
@ -2490,7 +2501,7 @@ X86CPU.prototype.fillPrefetch = function(n)
|
|||
{
|
||||
while (n-- > 0 && this.cbPrefetchQueued < X86CPU.PREFETCH.QUEUE) {
|
||||
var addr = this.addrPrefetchHead;
|
||||
var b = this.aMemBlocks[(addr & this.busMask) >> this.blockShift].readByte(addr & this.blockLimit);
|
||||
var b = this.aMemBlocks[(addr & this.busMask) >>> this.blockShift].readByte(addr & this.blockLimit);
|
||||
this.aPrefetch[this.iPrefetchHead] = b | (addr << 8);
|
||||
if (MAXDEBUG) this.printMessage(" fillPrefetch[" + this.iPrefetchHead + "]: " + str.toHex(addr) + ":" + str.toHexByte(b));
|
||||
this.addrPrefetchHead = (addr + 1) & this.busMask;
|
||||
|
|
@ -2939,9 +2950,9 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
|
|||
* back to the REP. To emulate this flawed behavior, turn on BUGS_8086.
|
||||
*/
|
||||
this.opLIP = this.regLIP;
|
||||
this.regEA = this.regEAWrite = X86.ADDR_INVALID;
|
||||
this.segData = this.segDS;
|
||||
this.segStack = this.segSS;
|
||||
this.regEA = this.regEAWrite = X86.ADDR_INVALID;
|
||||
|
||||
if (I386) {
|
||||
this.dataSize = this.segCS.dataSize;
|
||||
|
|
|
|||
|
|
@ -468,15 +468,15 @@ var X86Help = {
|
|||
* This instruction is always allowed to set MSW.PE, but it cannot clear MSW.PE once set;
|
||||
* therefore, we always OR the previous value of MSW.PE into the new value before loading.
|
||||
*/
|
||||
w |= (this.regMSW & X86.MSW.PE);
|
||||
this.regMSW = (this.regMSW & X86.MSW.SET) | (w & ~X86.MSW.SET);
|
||||
w |= (this.regCR0 & X86.CR0.MSW.PE);
|
||||
this.regCR0 = (this.regCR0 & X86.CR0.MSW.ON) | (w & ~X86.CR0.MSW.ON);
|
||||
/*
|
||||
* Since the 80286 cannot return to real-mode via this instruction, the only transition we
|
||||
* must worry about is to protected-mode. And don't worry, there's no harm calling setProtMode()
|
||||
* if the CPU is already in protected-mode (we could certainly optimize the call out in that
|
||||
* case, but this instruction isn't used frequently enough to warrant it).
|
||||
*/
|
||||
if (this.regMSW & X86.MSW.PE) this.setProtMode(true);
|
||||
if (this.regCR0 & X86.CR0.MSW.PE) this.setProtMode(true);
|
||||
},
|
||||
/**
|
||||
* opHelpCALLF(off, sel)
|
||||
|
|
@ -577,7 +577,7 @@ var X86Help = {
|
|||
* TODO: We assess a fixed cycle cost up front, because at the moment, switchTSS() doesn't assess anything.
|
||||
*/
|
||||
this.nStepCycles -= this.CYCLES.nOpCyclesIRet;
|
||||
if (this.regMSW & X86.MSW.PE) {
|
||||
if (this.regCR0 & X86.CR0.MSW.PE) {
|
||||
if (this.regPS & X86.PS.NT) {
|
||||
var addrNew = this.segTSS.base;
|
||||
var sel = this.getShort(addrNew + X86.TSS.PREV_TSS);
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ var X86Op0F = {
|
|||
/*
|
||||
* TODO: LOADALL operation still needs to be verified in protected mode....
|
||||
*/
|
||||
if (DEBUG && DEBUGGER && (this.regMSW & X86.MSW.PE)) this.stopCPU();
|
||||
if (DEBUG && DEBUGGER && (this.regCR0 & X86.CR0.MSW.PE)) this.stopCPU();
|
||||
},
|
||||
/**
|
||||
* @this {X86CPU}
|
||||
|
|
@ -177,7 +177,7 @@ var X86Op0F = {
|
|||
X86Help.opHelpFault.call(this, X86.EXCEPTION.GP_FAULT, 0, true);
|
||||
return;
|
||||
}
|
||||
this.regMSW &= ~X86.MSW.TS;
|
||||
this.regCR0 &= ~X86.CR0.MSW.TS;
|
||||
this.nStepCycles -= 2;
|
||||
},
|
||||
/**
|
||||
|
|
@ -429,7 +429,7 @@ var X86Op0F = {
|
|||
*/
|
||||
opSMSW: function(dst, src) {
|
||||
this.nStepCycles -= (2 + (this.regEA < 0? 0 : 1));
|
||||
return this.regMSW;
|
||||
return this.regCR0;
|
||||
},
|
||||
/**
|
||||
* @this {X86CPU}
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ X86Seg.loadIDTProt = function loadIDTProt(nIDT)
|
|||
*/
|
||||
X86Seg.checkReadReal = function checkReadReal(off, cb, fSuppress)
|
||||
{
|
||||
return this.base + off;
|
||||
return (this.base + off) | 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -272,7 +272,7 @@ X86Seg.checkReadReal = function checkReadReal(off, cb, fSuppress)
|
|||
*/
|
||||
X86Seg.checkWriteReal = function checkWriteReal(off, cb, fSuppress)
|
||||
{
|
||||
return this.base + off;
|
||||
return (this.base + off) | 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -467,7 +467,7 @@ X86Seg.switchTSS = function switchTSS(selNew, fNest)
|
|||
cpu.setSP(cpu.getShort(addrNew + offSP));
|
||||
cpu.segLDT.load(cpu.getShort(addrNew + X86.TSS.TASK_LDT));
|
||||
if (fNest) cpu.setShort(addrNew + X86.TSS.PREV_TSS, selOld);
|
||||
cpu.regMSW |= X86.MSW.TS;
|
||||
cpu.regCR0 |= X86.CR0.MSW.TS;
|
||||
return true;
|
||||
};
|
||||
|
||||
|
|
@ -760,16 +760,21 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
|
|||
* setBase(addr)
|
||||
*
|
||||
* This is used in unusual situations where the base must be set independently; normally, the base
|
||||
* is set according to the selector provided to load(), but there are a few cases where setBase() is
|
||||
* required (eg, in resetRegs(), where the 80286 wants the real-mode CS selector to be 0xF000 but the
|
||||
* CS base must be 0xFF0000).
|
||||
* is set according to the selector provided to load(), but there are a few cases where setBase()
|
||||
* is required.
|
||||
*
|
||||
* For example, in resetRegs(), the real-mode CS selector must be reset to 0xF000 for an 80286 or 80386,
|
||||
* but the CS base must be set to 0x00FF0000 or 0xFFFF0000, respectively. To simplify life for setBase()
|
||||
* callers, we allow them to specify 32-bit bases, which we then truncate to 24 bits as needed.
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} addr
|
||||
* @return {number} addr, truncated as needed
|
||||
*/
|
||||
X86Seg.prototype.setBase = function(addr)
|
||||
{
|
||||
this.base = addr;
|
||||
if (this.cpu.model < X86.MODEL_80386) addr &= 0xffffff;
|
||||
return this.base = addr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -825,7 +830,7 @@ X86Seg.prototype.restore = function(a)
|
|||
X86Seg.prototype.updateMode = function(fProt)
|
||||
{
|
||||
if (fProt === undefined) {
|
||||
fProt = !!(this.cpu.regMSW & X86.MSW.PE);
|
||||
fProt = !!(this.cpu.regCR0 & X86.CR0.MSW.PE);
|
||||
}
|
||||
this.fExpDown = false;
|
||||
if (fProt) {
|
||||
|
|
|
|||
|
|
@ -996,7 +996,7 @@
|
|||
<xsl:call-template name="component">
|
||||
<xsl:with-param name="machine" select="$machine"/>
|
||||
<xsl:with-param name="class">computer</xsl:with-param>
|
||||
<xsl:with-param name="parms">,buswidth:'<xsl:value-of select="$buswidth"/>',resume:'<xsl:value-of select="$resume"/>',state:'<xsl:value-of select="$state"/>'</xsl:with-param>
|
||||
<xsl:with-param name="parms">,buswidth:<xsl:value-of select="$buswidth"/>,resume:<xsl:value-of select="$resume"/>,state:'<xsl:value-of select="$state"/>'</xsl:with-param>
|
||||
</xsl:call-template>
|
||||
</xsl:template>
|
||||
|
||||
|
|
|
|||
|
|
@ -137,12 +137,12 @@ net.propagateParms = function(sURL, req)
|
|||
/**
|
||||
* encodeURL(sURL, req, fDebug)
|
||||
*
|
||||
* Used to encodes any URLs presented on the current page, using this 3-step process:
|
||||
* Used to encodes any URLs presented on the current page, using this 3-step (um, 4-step) process:
|
||||
*
|
||||
* 1) Replace any backslashes with slashes, in case the URL was derived from a file system path
|
||||
* 2) Remap links that begin with "static/" to the corresponding URL at "http://static.pcjs.org/"
|
||||
* 3) Massage the result with net.propagateParms(), so that any special parameters are passed along
|
||||
* 4) Transform any "htmlspecialchars" into the corresponding entities, to help ensure proper validation
|
||||
* 3) Transform any "htmlspecialchars" into the corresponding entities, using encodeURI()
|
||||
* 4) Massage the result with net.propagateParms(), so that any special parameters are passed along
|
||||
*
|
||||
* @param {string} sURL
|
||||
* @param {Object} req is the web server's (ie, Express) request object, if any
|
||||
|
|
@ -154,8 +154,9 @@ net.encodeURL = function(sURL, req, fDebug)
|
|||
if (sURL) {
|
||||
sURL = sURL.replace(/\\/g, '/');
|
||||
if (!fDebug) {
|
||||
if (sURL.match(/^[^:?]*static/)) {
|
||||
sURL = "http://static.pcjs.org" + path.join(req.path, sURL).replace("/static/", "/");
|
||||
if (sURL.match(/^[^:?]*static\//)) {
|
||||
if (sURL.charAt(0) != '/') sURL = path.join(req.path, sURL);
|
||||
sURL = "http://static.pcjs.org" + sURL.replace("/static/", "/");
|
||||
}
|
||||
}
|
||||
return net.propagateParms(encodeURI(sURL), req);
|
||||
|
|
|
|||
Loading…
Reference in a new issue