Debugger changes for improved 32-bit support
This commit is contained in:
parent
9ba73970a4
commit
f50612e7ca
21 changed files with 3581 additions and 2949 deletions
|
|
@ -68,20 +68,24 @@
|
|||
* avoid display problems near the right edge. BASIC will let you choose a width SMALLER than
|
||||
* 24 but not larger. So, while the video buffer supports a theoretical maximum of 32 rows x 32
|
||||
* columns, the practical maximum is 25 rows x 24 columns; the last 4 rows of the video buffer
|
||||
* are never used, and while content DOES scroll through the top 4 lines of the buffer, it should
|
||||
* are never used, and while content DOES scroll through the top 3 lines of the buffer, it should
|
||||
* never be assumed that you can see the top 3 lines.
|
||||
*
|
||||
* This is partially confirmed by the "C1P Character Graphics Reference Manual", p3, which says
|
||||
* that the "the visible character field consists of 25 lines of 25 columns" and that the "first
|
||||
* visible character in the upper left of the screen is accessed via address 53379," or 0xD083.
|
||||
* However, they were wrong about both the number of columns and the first visible character.
|
||||
*
|
||||
* They actually meant 0xD085, because as mentioned earlier, the C1P indents every row by 5
|
||||
* characters, not 3. Even so, the difference between 0xD365 (where the bottom line starts)
|
||||
* and 0xD085 is 0x2E0, or 736. And 736 divided by 32 gives 23; add the bottom row, and that
|
||||
* gives you 24 visible rows, not 25. Since we now have screenshots of a C1P monitor displaying
|
||||
* 25 rows (courtesy of Stephan Mühlstrasser <stephan.muehlstrasser@web.de>), C1Pjs now assumes
|
||||
* 25 rows, which means that only the first 3 lines are not visible, which necessarily puts
|
||||
* the address of the first visible character at 0xD065.
|
||||
* They probably meant 0xD085, because as mentioned earlier, the C1P indents every row by 5
|
||||
* characters, not 3. But that's not correct either, because the difference between 0xD365
|
||||
* (where the bottom line starts) and 0xD085 is 0x2E0, or 736. 736 divided by 32 equals 23;
|
||||
* add the bottom row, and that would give you 24 visible rows, not 25. Since we now have
|
||||
* screenshots of a C1P monitor displaying 25 rows (courtesy of Stephan Mühlstrasser), C1Pjs
|
||||
* now assumes that only the first 3 lines are not visible, and that the address of the first
|
||||
* visible character is actually 0xD065 (53349), yielding 25 visible rows.
|
||||
*
|
||||
* All of this explains why we now use setDimensions(iRowTop=3, nRowsVisible=25) instead of
|
||||
* setDimensions(iRowTop=4, nRowsVisible=24) for the Model 600.
|
||||
*
|
||||
* Model 540 Video Board vs. Model 600 "Superboard II"
|
||||
* ---------------------------------------------------
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -125,6 +125,13 @@ var BUGS_8086 = false;
|
|||
*/
|
||||
var I386 = true;
|
||||
|
||||
/**
|
||||
* @define {boolean}
|
||||
*
|
||||
* Enables Compaq DeskPro 386 support.
|
||||
*/
|
||||
var COMPAQ386 = true;
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
global.PCJSCLASS = PCJSCLASS;
|
||||
global.DEBUGGER = DEBUGGER;
|
||||
|
|
@ -135,6 +142,7 @@ if (typeof module !== 'undefined') {
|
|||
global.SAMPLER = SAMPLER;
|
||||
global.BUGS_8086 = BUGS_8086;
|
||||
global.I386 = I386;
|
||||
global.COMPAQ386 = COMPAQ386;
|
||||
/*
|
||||
* TODO: When we're "required" by Node, should we return anything via module.exports?
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -218,7 +218,8 @@ Memory.TYPE = {
|
|||
RAM: 1,
|
||||
ROM: 2,
|
||||
VIDEO: 3,
|
||||
NAMES: ["NONE", "RAM", "ROM", "VIDEO"],
|
||||
CTRL: 4,
|
||||
NAMES: ["NONE", "RAM", "ROM", "VIDEO", "H/W"],
|
||||
COLORS: ["black", "blue", "green", "cyan"]
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -447,17 +447,18 @@ Panel.prototype.updateMouse = function(event, fDown)
|
|||
this.yMouse = y;
|
||||
|
||||
if (MAXDEBUG) this.log("Panel.moveMouse(" + x + "," + y + ")");
|
||||
this.assert(x >= 0 && x < Panel.LIVECANVAS.CX && y >= 0 && y < Panel.LIVECANVAS.CY);
|
||||
|
||||
/*
|
||||
* Convert the mouse position into the corresponding memory address, assuming it's over the live memory area
|
||||
*/
|
||||
var addr = this.findAddress(x, y);
|
||||
if (addr != X86.ADDR_INVALID) {
|
||||
addr &= ~0xf;
|
||||
if (addr != this.addrDumpLast) {
|
||||
this.dumpMemory(addr, true);
|
||||
this.addrDumpLast = addr;
|
||||
if (x >= 0 && x < Panel.LIVECANVAS.CX && y >= 0 && y < Panel.LIVECANVAS.CY) {
|
||||
/*
|
||||
* Convert the mouse position into the corresponding memory address, assuming it's over the live memory area
|
||||
*/
|
||||
var addr = this.findAddress(x, y);
|
||||
if (addr != X86.ADDR_INVALID) {
|
||||
addr &= ~0xf;
|
||||
if (addr != this.addrDumpLast) {
|
||||
this.dumpMemory(addr, true);
|
||||
this.addrDumpLast = addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ if (typeof module !== 'undefined') {
|
|||
* @extends Component
|
||||
* @param {Object} parmsRAM
|
||||
*/
|
||||
function RAM(parmsRAM) {
|
||||
|
||||
function RAM(parmsRAM)
|
||||
{
|
||||
Component.call(this, "RAM", parmsRAM, RAM);
|
||||
|
||||
this.addrRAM = parmsRAM['addr'];
|
||||
|
|
@ -76,7 +76,8 @@ Component.subclass(Component, RAM);
|
|||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
*/
|
||||
RAM.prototype.initBus = function(cmp, bus, cpu, dbg) {
|
||||
RAM.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.chipset = cmp.getComponentByType("ChipSet");
|
||||
|
|
@ -91,7 +92,8 @@ RAM.prototype.initBus = function(cmp, bus, cpu, dbg) {
|
|||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
RAM.prototype.powerUp = function(data, fRepower) {
|
||||
RAM.prototype.powerUp = function(data, fRepower)
|
||||
{
|
||||
if (!fRepower) {
|
||||
/*
|
||||
* The Computer powers up the CPU last, at which point the X86 state is restored,
|
||||
|
|
@ -117,7 +119,8 @@ RAM.prototype.powerUp = function(data, fRepower) {
|
|||
* @param {boolean} fSave
|
||||
* @return {Object|boolean}
|
||||
*/
|
||||
RAM.prototype.powerDown = function(fSave) {
|
||||
RAM.prototype.powerDown = function(fSave)
|
||||
{
|
||||
/*
|
||||
* The Computer powers down the CPU first, at which point the X86 state is saved,
|
||||
* which includes the Bus state, and since we use the Bus component to allocate all
|
||||
|
|
@ -148,7 +151,8 @@ RAM.prototype.powerDown = function(fSave) {
|
|||
*
|
||||
* @this {RAM}
|
||||
*/
|
||||
RAM.prototype.reset = function() {
|
||||
RAM.prototype.reset = function()
|
||||
{
|
||||
if (!this.addrRAM && !this.fInstalled && this.chipset) {
|
||||
var baseRAM = this.chipset.getSWMemorySize() * 1024;
|
||||
if (this.sizeRAM && baseRAM != this.sizeRAM) {
|
||||
|
|
@ -168,6 +172,30 @@ RAM.prototype.reset = function() {
|
|||
* for these components, which the Computer component will display as it "powers up" components.
|
||||
*/
|
||||
if (MAXDEBUG && this.fInstalled) this.status("specified size overrides SW1");
|
||||
/*
|
||||
* Memory with an ID of "ramCPQ" is reserved for built-in memory located just below the 16Mb
|
||||
* boundary on Compaq DeskPro 386 machines.
|
||||
*
|
||||
* Technically, that memory is part of the first 1Mb of memory that also provides up to 640Kb
|
||||
* of conventional memory (ie, memory below 1Mb).
|
||||
*
|
||||
* However, PCjs doesn't support individual memory allocations that (a) are discontiguous
|
||||
* or (b) dynamically change location. Components must simulate those features by performing
|
||||
* a separate allocation for each starting address, and removing/adding memory allocations
|
||||
* whenever their starting address changes.
|
||||
*
|
||||
* Therefore, a DeskPro 386's first 1Mb of physical memory is allocated by PCjs in two pieces,
|
||||
* and the second piece must have an ID of "ramCPQ", triggering the additional allocation of
|
||||
* Compaq-specific memory-mapped registers.
|
||||
*
|
||||
* See CompaqController for more details.
|
||||
*/
|
||||
if (COMPAQ386) {
|
||||
if (this.idComponent == "ramCPQ") {
|
||||
this.controller = new CompaqController(this);
|
||||
this.bus.addMemory(CompaqController.ADDR, 1, Memory.TYPE.CTRL, this.controller);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.fAllocated) {
|
||||
|
|
@ -193,7 +221,8 @@ RAM.prototype.reset = function() {
|
|||
* attribute, invoking the constructor to create a RAM component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
RAM.init = function() {
|
||||
RAM.init = function()
|
||||
{
|
||||
var aeRAM = Component.getElementsByClass(window.document, PCJSCLASS, "ram");
|
||||
for (var iRAM = 0; iRAM < aeRAM.length; iRAM++) {
|
||||
var eRAM = aeRAM[iRAM];
|
||||
|
|
@ -203,6 +232,121 @@ RAM.init = function() {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* CompaqController(ram)
|
||||
*
|
||||
* DeskPro 386 machines came with a minimum of 1Mb of RAM, which could be configured (via jumpers)
|
||||
* for 256Kb, 512Kb or 640Kb of conventional memory, starting at address 0x00000000, with the
|
||||
* remainder (768Kb, 512Kb, or 384Kb) accessible only at addresses just below 0x01000000. This
|
||||
* second chunk of RAM must have an ID of "ramCPQ".
|
||||
*
|
||||
* The typical configuration was 640Kb of conventional memory, leaving 384Kb accessible at 0x00FA0000.
|
||||
* Presumably, the other configurations (256Kb and 512Kb) would leave 768Kb and 512Kb accessible at
|
||||
* 0x00F40000 and 0x00F80000, respectively.
|
||||
*
|
||||
* The DeskPro 386 also contained two memory-mapped registers at 0x80C00000. The first is a write-only
|
||||
* mapping register that provides the ability to map the 128Kb at 0x00FE0000 to 0x000E0000, replacing
|
||||
* any ROMs in the range 0x000E0000-0x000FFFFF, and optionally write-protecting that 128Kb. The second
|
||||
* register is a read-only diagnostics register that indicates jumper configuration and parity errors.
|
||||
*
|
||||
* To emulate the memory-mapped registers at 0x80C00000, the RAM component allocates a block at that
|
||||
* address using this custom controller once it sees an allocation for "ramCPQ".
|
||||
*
|
||||
* Later, when the addressibility of "ramCPQ" memory is altered, we record the blocks in all the
|
||||
* memory slots spanning 0x000E0000-0x000FFFFF, and then update those slots with the blocks from
|
||||
* 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.
|
||||
*
|
||||
* @constructor
|
||||
* @param {RAM} ram
|
||||
*/
|
||||
function CompaqController(ram)
|
||||
{
|
||||
this.ram = ram;
|
||||
this.bMapping = CompaqController.MAPPING.DEFAULT;
|
||||
this.bSettings = CompaqController.SETTINGS.BASE_640KB;
|
||||
}
|
||||
|
||||
CompaqController.ADDR = 0x80C00000;
|
||||
|
||||
/*
|
||||
* Bit definitions for the 8-bit write-only memory-mapping register (bMapping)
|
||||
*/
|
||||
CompaqController.MAPPING = {
|
||||
NORELOC: 0x01, // is this bit is CLEAR, the last 128Kb (at 0x00FE0000) is relocated to 0x000E0000
|
||||
READWRITE: 0x02, // if this bit is CLEAR, the last 128Kb (at 0x00FE0000) is read-only (ie, write-protected)
|
||||
RESERVED: 0xFC, // the remaining 6 bits are reserved and should always be SET
|
||||
DEFAULT: 0xFF
|
||||
};
|
||||
|
||||
/*
|
||||
* Bit definitions for the 8-bit read-only settings/diagnostics register (bSettings)
|
||||
*/
|
||||
CompaqController.SETTINGS = {
|
||||
B0_PARITY: 0x01,
|
||||
B1_PARITY: 0x02,
|
||||
B2_PARITY: 0x04,
|
||||
B3_PARITY: 0x08,
|
||||
BASE_640KB: 0x00,
|
||||
BASE_ERROR: 0x10,
|
||||
BASE_512KB: 0x20,
|
||||
BASE_256KB: 0x30,
|
||||
ADDED_1MB: 0x40,
|
||||
PIGGYBACK: 0x80
|
||||
};
|
||||
|
||||
/**
|
||||
* readByte(off)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @return {number}
|
||||
*/
|
||||
CompaqController.readByte = function readCompaqControllerByte(off)
|
||||
{
|
||||
return this.controller.bSettings;
|
||||
};
|
||||
|
||||
/**
|
||||
* writeByte(off, b)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {number} off
|
||||
* @param {number} b (which should already be pre-masked to 8 bits; see Bus.prototype.setByteDirect)
|
||||
*/
|
||||
CompaqController.writeByte = function writeCompaqControllerByte(off, b)
|
||||
{
|
||||
this.controller.bMapping = b;
|
||||
};
|
||||
|
||||
CompaqController.ACCESS = [CompaqController.readByte, CompaqController.readByte, CompaqController.readByte,
|
||||
CompaqController.writeByte, CompaqController.writeByte, CompaqController.writeByte];
|
||||
|
||||
/**
|
||||
* getMemoryBuffer(addr)
|
||||
*
|
||||
* @this {CompaqController}
|
||||
* @param {number} addr
|
||||
* @return {Array} containing the buffer (and an offset within that buffer)
|
||||
*/
|
||||
CompaqController.prototype.getMemoryBuffer = function(addr)
|
||||
{
|
||||
return [null, 0];
|
||||
};
|
||||
|
||||
/**
|
||||
* getMemoryAccess()
|
||||
*
|
||||
* @this {CompaqController}
|
||||
* @return {Array.<function()>}
|
||||
*/
|
||||
CompaqController.prototype.getMemoryAccess = function()
|
||||
{
|
||||
return CompaqController.ACCESS;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize all the RAM modules on the page.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -68,7 +68,20 @@ function ROM(parmsROM)
|
|||
this.abROM = null;
|
||||
this.addrROM = parmsROM['addr'];
|
||||
this.sizeROM = parmsROM['size'];
|
||||
this.addrROMAlias = parmsROM['alias'];
|
||||
|
||||
/*
|
||||
* The new 'alias' property can now be EITHER a single physical address (like 'addr') OR an array of
|
||||
* physical addresses; eg:
|
||||
*
|
||||
* [0xf0000,0xffff0000,0xffff8000]
|
||||
*
|
||||
* We could have overloaded 'addr' to accomplish the same thing, but I think it's better to have any
|
||||
* aliased locations listed under a separate property.
|
||||
*
|
||||
* Most ROMs are not aliased, in which case the 'alias' property should have the default value of null.
|
||||
*/
|
||||
this.addrAlias = parmsROM['alias'];
|
||||
|
||||
this.sFilePath = parmsROM['file'];
|
||||
this.sFileName = str.getBaseName(this.sFilePath);
|
||||
this.idNotify = parmsROM['notify'];
|
||||
|
|
@ -274,7 +287,17 @@ ROM.prototype.copyROM = function()
|
|||
*/
|
||||
this.setError("ROM size (0x" + str.toHex(this.abROM.length) + ") does not match specified size (0x" + str.toHex(this.sizeROM) + ")");
|
||||
}
|
||||
else if (this.addROM(this.addrROM) && this.addROM(this.addrROMAlias)) {
|
||||
else if (this.addROM(this.addrROM)) {
|
||||
|
||||
var aliases = [];
|
||||
if (typeof this.addrAlias == "number") {
|
||||
aliases.push(this.addrAlias);
|
||||
} else if (this.addrAlias != null && this.addrAlias.length) {
|
||||
aliases = this.addrAlias;
|
||||
}
|
||||
for (var i = 0; i < aliases.length; i++) {
|
||||
this.addROM(aliases[i]);
|
||||
}
|
||||
/*
|
||||
* If there's a component we should notify, notify it now, and give it the internal byte array, so that
|
||||
* it doesn't have to ask the CPU for the data. Currently, the only component that uses this notification
|
||||
|
|
@ -306,16 +329,12 @@ ROM.prototype.copyROM = function()
|
|||
/**
|
||||
* addROM(addr)
|
||||
*
|
||||
* If addr is null or undefined, then it's presumably an unused addrROMAlias, which we simply ignore (it's not
|
||||
* considered a failure condition).
|
||||
*
|
||||
* @this {ROM}
|
||||
* @param {number} addr
|
||||
* @return {boolean}
|
||||
*/
|
||||
ROM.prototype.addROM = function(addr)
|
||||
{
|
||||
if (addr == null) return true;
|
||||
if (this.bus.addMemory(addr, this.sizeROM, Memory.TYPE.ROM)) {
|
||||
if (DEBUG) this.log("addROM(): copying ROM to 0x" + str.toHex(addr) + " (0x" + str.toHex(this.abROM.length) + " bytes)");
|
||||
var bto = null;
|
||||
|
|
|
|||
|
|
@ -84,11 +84,12 @@ var X86 = {
|
|||
* 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)
|
||||
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)
|
||||
MASK: 0xffff // these are the only (MSW) bits that the 80286 can access (within CR0)
|
||||
},
|
||||
ET: 0x00000010, // coprocessor type (80287 or 80387); always 1 on post-80386 CPUs
|
||||
PG: 0x80000000|0 // 0: paging disabled
|
||||
|
|
@ -147,7 +148,7 @@ var X86 = {
|
|||
SHIFT: 13
|
||||
},
|
||||
PRESENT: 0x8000,
|
||||
INVALID: 0 // use X86.DESC.ACC.INVALID for invalid ACC values
|
||||
INVALID: 0 // use X86.DESC.ACC.INVALID for invalid ACC values
|
||||
},
|
||||
EXT: { // descriptor extension word (reserved on the 80286; "must be zero")
|
||||
OFFSET: 0x6,
|
||||
|
|
|
|||
|
|
@ -706,9 +706,9 @@ X86CPU.prototype.initProcessor = function()
|
|||
*/
|
||||
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
|
||||
this.aOpGrp4b = X86Grps.aOpGrp4b.slice();
|
||||
this.aOpGrp4w = X86Grps.aOpGrp4w.slice();
|
||||
this.aOps = X86OpXX.aOps;
|
||||
this.aOpGrp4b = X86Grps.aOpGrp4b;
|
||||
this.aOpGrp4w = X86Grps.aOpGrp4w;
|
||||
this.aOpGrp6 = X86Op0F.aOpGrp6Real; // setProtMode() will ensure that aOpGrp6 is switched
|
||||
|
||||
if (this.model >= X86.MODEL_80186) {
|
||||
|
|
@ -720,6 +720,9 @@ X86CPU.prototype.initProcessor = function()
|
|||
* Instruction handlers that contain "hard-coded" 80286 cycle times include: opINSb, opINSw, opOUTSb,
|
||||
* opOUTSw, opENTER, and opLEAVE.
|
||||
*/
|
||||
this.aOps = X86OpXX.aOps.slice(); // make copies of aOps and others before modifying them
|
||||
this.aOpGrp4b = X86Grps.aOpGrp4b.slice();
|
||||
this.aOpGrp4w = X86Grps.aOpGrp4w.slice();
|
||||
this.nShiftCountMask = 0x1f; // on newer processors, all shift counts are MOD 32
|
||||
this.aOps[0x0F] = X86Help.opHelpInvalid;
|
||||
this.aOps[X86.OPCODE.PUSHA] = X86OpXX.opPUSHA;
|
||||
|
|
@ -753,16 +756,38 @@ X86CPU.prototype.initProcessor = function()
|
|||
|
||||
this.OPFLAG_NOINTR8086 = 0; // used with instructions that should *not* set NOINTR on an 80286 (eg, non-SS segment loads)
|
||||
|
||||
this.aOps0F = X86Op0F.aOps0F;
|
||||
this.aOps[0x0F] = X86OpXX.op0F;
|
||||
this.aOps[X86.OPCODE.ARPL] = X86OpXX.opARPL;
|
||||
this.aOps[X86.OPCODE.PUSHSP] = X86OpXX.opPUSHSP;
|
||||
|
||||
if (I386 && this.model >= X86.MODEL_80386) {
|
||||
this.aOps[X86.OPCODE.FS] = X86OpXX.opFS;
|
||||
this.aOps[X86.OPCODE.GS] = X86OpXX.opGS;
|
||||
this.aOps[X86.OPCODE.OS] = X86OpXX.opOS;
|
||||
this.aOps[X86.OPCODE.AS] = X86OpXX.opAS;
|
||||
this.aOps0F = X86Op0F.aOps0F.slice();
|
||||
this.aOps0F[0x20] = X86Op0F.opMOVrcr;
|
||||
this.aOps0F[0x22] = X86Op0F.opMOVcrr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
this.aaOpMem = [];
|
||||
this.aaOpMem[2] = {
|
||||
getWord: this.getShort.bind(this),
|
||||
setWord: this.setShort.bind(this)
|
||||
};
|
||||
if (I386) {
|
||||
this.aaOpMem[4] = {
|
||||
getWord: this.getLong.bind(this),
|
||||
setWord: this.setLong.bind(this)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -928,35 +953,10 @@ X86CPU.prototype.resetRegs = function()
|
|||
*/
|
||||
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 (!I386) this.setSizes();
|
||||
|
||||
if (BACKTRACK) {
|
||||
/*
|
||||
* Initialize the backtrack indexes for all registers to zero. And while, yes, it IS possible
|
||||
|
|
@ -1029,35 +1029,17 @@ X86CPU.prototype.resetRegs = function()
|
|||
* Now that all the segment registers have been created, it's safe to set the current addressing mode.
|
||||
*/
|
||||
this.setProtMode();
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
this.aaOpMem = [];
|
||||
this.aaOpMem[2] = {
|
||||
getWord: this.getShort.bind(this),
|
||||
setWord: this.setShort.bind(this)
|
||||
};
|
||||
if (I386) {
|
||||
this.aaOpMem[4] = {
|
||||
getWord: this.getLong.bind(this),
|
||||
setWord: this.setLong.bind(this)
|
||||
};
|
||||
}
|
||||
this.opMem = this.aaOpMem[this.segCS.dataSize];
|
||||
this.setOpMod();
|
||||
};
|
||||
|
||||
/**
|
||||
* setOpMod()
|
||||
* setAddrSize()
|
||||
*
|
||||
* Select the appropriate ModRM dispatch tables, based on the current ADDRESS size (addrSize), which
|
||||
* is based foremost on segCS.addrSize, but can also be overridden by an ADDRESS size instruction prefix.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
X86CPU.prototype.setOpMod = function()
|
||||
X86CPU.prototype.setAddrSize = function()
|
||||
{
|
||||
if (!I386) {
|
||||
this.aOpModRegByte = X86ModB.aOpModReg;
|
||||
|
|
@ -1085,6 +1067,54 @@ X86CPU.prototype.setOpMod = function()
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* setDataSize()
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
X86CPU.prototype.setDataSize = function()
|
||||
{
|
||||
this.opMem = this.aaOpMem[this.dataSize];
|
||||
};
|
||||
|
||||
/**
|
||||
* setSizes()
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
X86CPU.prototype.setSizes = function()
|
||||
{
|
||||
/*
|
||||
* 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 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.setAddrSize();
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
this.setDataSize();
|
||||
};
|
||||
|
||||
/**
|
||||
* getChecksum()
|
||||
*
|
||||
|
|
@ -1230,6 +1260,10 @@ X86CPU.prototype.setProtMode = function(fProt)
|
|||
this.segDS.updateMode(fProt);
|
||||
this.segSS.updateMode(fProt);
|
||||
this.segES.updateMode(fProt);
|
||||
if (I386 && this.model >= X86.MODEL_80386) {
|
||||
this.segFS.updateMode(fProt);
|
||||
this.segGS.updateMode(fProt);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1303,8 +1337,7 @@ X86CPU.prototype.save = function()
|
|||
*/
|
||||
X86CPU.prototype.restore = function(data)
|
||||
{
|
||||
var a;
|
||||
a = data[0];
|
||||
var a = data[0];
|
||||
this.regEAX = a[0];
|
||||
this.regEBX = a[1];
|
||||
this.regECX = a[2];
|
||||
|
|
@ -1314,6 +1347,7 @@ X86CPU.prototype.restore = function(data)
|
|||
this.regESI = a[6];
|
||||
this.regEDI = a[7];
|
||||
this.nIOPL = a[8] || 0;
|
||||
|
||||
a = data[1];
|
||||
this.segCS.restore(a[1]);
|
||||
this.segDS.restore(a[2]);
|
||||
|
|
@ -1321,21 +1355,24 @@ X86CPU.prototype.restore = function(data)
|
|||
this.segES.restore(a[4]);
|
||||
this.restoreProtMode(a[5]);
|
||||
this.setPS(a[6]);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* It's important to call setCSIP(), both to ensure that the CPU's linear IP register (regLIP) is updated
|
||||
* properly AND to ensure the CPU's default ADDRESS and OPERAND sizes are set properly.
|
||||
*/
|
||||
this.setIP(a[0]);
|
||||
this.setCSIP(a[0], this.segCS.sel);
|
||||
/*
|
||||
* 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]);
|
||||
}
|
||||
|
||||
a = data[2];
|
||||
this.segData = a[0] != null && this.getSeg(a[0]) || this.segDS;
|
||||
this.segStack = a[1] != null && this.getSeg(a[1]) || this.segSS;
|
||||
|
|
@ -1344,6 +1381,7 @@ X86CPU.prototype.restore = function(data)
|
|||
this.intFlags = a[4];
|
||||
this.regEA = a[5];
|
||||
this.regEAWrite = a[6]; // save/restore of last EA calculation(s) isn't strictly necessary, but they may be of some interest to, say, the Debugger
|
||||
|
||||
a = data[3]; // a[0] was previously nBurstDivisor (no longer used)
|
||||
this.nTotalCycles = a[1];
|
||||
this.setSpeed(a[2]); // if we're restoring an old state that doesn't contain a value from getSpeed(), that's OK; setSpeed() checks for an undefined value
|
||||
|
|
@ -1407,6 +1445,7 @@ X86CPU.prototype.setCS = function(sel)
|
|||
var regEIP = this.getIP();
|
||||
this.regLIP = this.segCS.load(sel) + regEIP;
|
||||
this.regLIPLimit = this.segCS.base + this.segCS.limit;
|
||||
if (I386) this.setSizes();
|
||||
if (!BUGS_8086) this.opFlags |= this.OPFLAG_NOINTR8086;
|
||||
if (PREFETCH) this.flushPrefetch(this.regLIP);
|
||||
};
|
||||
|
|
@ -1593,7 +1632,6 @@ X86CPU.prototype.setIP = function(off)
|
|||
*/
|
||||
X86CPU.prototype.setCSIP = function(off, sel, fCall)
|
||||
{
|
||||
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
|
||||
|
|
@ -1607,6 +1645,7 @@ X86CPU.prototype.setCSIP = function(off, sel, fCall)
|
|||
if (base != X86.ADDR_INVALID) {
|
||||
this.regLIP = base + this.regEIP;
|
||||
this.regLIPLimit = base + this.segCS.limit;
|
||||
if (I386) this.setSizes();
|
||||
if (PREFETCH) this.flushPrefetch(this.regLIP);
|
||||
return this.segCS.fStackSwitch;
|
||||
}
|
||||
|
|
@ -2983,14 +3022,14 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
|
|||
var fDebugCheck = this.aFlags.fDebugCheck = (DEBUGGER && this.dbg && this.dbg.checksEnabled());
|
||||
|
||||
/*
|
||||
* fDebugSkip is checked only when fDebugCheck is true, and its sole purpose is to tell the first call
|
||||
* nDebugState is checked only when fDebugCheck is true, and its sole purpose is to tell the first call
|
||||
* to checkInstruction() that it can skip breakpoint checks, and that will be true ONLY when fStarting is
|
||||
* true OR nMinCycles is zero (the latter means the Debugger is single-stepping).
|
||||
*
|
||||
* Once we snap fStarting, we clear it, because technically, we've moved beyond "starting" and have
|
||||
* officially "started" now.
|
||||
*/
|
||||
var fDebugSkip = this.aFlags.fStarting || !nMinCycles;
|
||||
var nDebugState = nMinCycles == 0? -1 : (this.aFlags.fStarting? 0 : 1);
|
||||
this.aFlags.fStarting = false;
|
||||
|
||||
/*
|
||||
|
|
@ -3044,13 +3083,7 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
|
|||
this.segStack = this.segSS;
|
||||
this.regEA = this.regEAWrite = X86.ADDR_INVALID;
|
||||
|
||||
if (I386) {
|
||||
this.dataSize = this.segCS.dataSize;
|
||||
this.dataMask = this.segCS.dataMask;
|
||||
this.addrSize = this.segCS.addrSize;
|
||||
this.addrMask = this.segCS.addrMask;
|
||||
this.opMem = this.aaOpMem[this.dataSize];
|
||||
}
|
||||
if (I386) this.setSizes();
|
||||
|
||||
this.opPrefixes = this.opFlags & X86.OPFLAG.REPEAT;
|
||||
if (this.intFlags) {
|
||||
|
|
@ -3088,11 +3121,11 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
|
|||
}
|
||||
|
||||
if (DEBUGGER && fDebugCheck) {
|
||||
if (this.dbg.checkInstruction(this.regLIP, fDebugSkip)) {
|
||||
if (this.dbg.checkInstruction(this.regLIP, nDebugState)) {
|
||||
this.stopCPU();
|
||||
break;
|
||||
}
|
||||
fDebugSkip = false;
|
||||
nDebugState = 1;
|
||||
}
|
||||
|
||||
if (SAMPLER) {
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ var X86Help = {
|
|||
* @param {number} src (new value)
|
||||
* @return {number} dst (src is overridden, replaced with regMD16, as specified by opMOVwsr())
|
||||
*/
|
||||
opHelpMOVSegSrc: function(dst, src) {
|
||||
opHelpMOVMD16: function(dst, src) {
|
||||
return X86Help.opHelpMOV.call(this, dst, this.regMD16);
|
||||
},
|
||||
/**
|
||||
|
|
@ -468,8 +468,8 @@ 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.regCR0 & X86.CR0.MSW.PE);
|
||||
this.regCR0 = (this.regCR0 & X86.CR0.MSW.ON) | (w & ~X86.CR0.MSW.ON);
|
||||
w |= (this.regCR0 & X86.CR0.MSW.PE) | X86.CR0.MSW.ON;
|
||||
this.regCR0 = (this.regCR0 & ~X86.CR0.MSW.MASK) | (w & X86.CR0.MSW.MASK);
|
||||
/*
|
||||
* 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()
|
||||
|
|
@ -478,6 +478,20 @@ var X86Help = {
|
|||
*/
|
||||
if (this.regCR0 & X86.CR0.MSW.PE) this.setProtMode(true);
|
||||
},
|
||||
/**
|
||||
* opHelpLCR0(l)
|
||||
*
|
||||
* This called on behalf of 80386 opcodes only (ie, MOV CR0,reg).
|
||||
*
|
||||
* TODO: Determine which CR0 bits, if any, cannot be modified by MOV CR0,reg.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} l
|
||||
*/
|
||||
opHelpLCR0: function(l) {
|
||||
this.regCR0 = l;
|
||||
this.setProtMode(!!(this.regCR0 & X86.CR0.MSW.PE));
|
||||
},
|
||||
/**
|
||||
* opHelpCALLF(off, sel)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ if (typeof module !== 'undefined') {
|
|||
|
||||
var X86Op0F = {
|
||||
/**
|
||||
* @this {X86CPU}
|
||||
* op=0x0F,0x00 (GRP6 rm)
|
||||
*
|
||||
* op=0x0F,0x00 (grp6 rm)
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
opGrp6: function() {
|
||||
var bModRM = this.getIPByte();
|
||||
|
|
@ -52,9 +52,9 @@ var X86Op0F = {
|
|||
this.aOpModGrpWord[bModRM].call(this, this.aOpGrp6, X86Grps.opGrpNoSrc);
|
||||
},
|
||||
/**
|
||||
* @this {X86CPU}
|
||||
* op=0x0F,0x01 (GRP7 rm)
|
||||
*
|
||||
* op=0x0F,0x01 (grp7 rm)
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
opGrp7: function() {
|
||||
var bModRM = this.getIPByte();
|
||||
|
|
@ -64,17 +64,21 @@ var X86Op0F = {
|
|||
this.aOpModGrpWord[bModRM].call(this, X86Op0F.aOpGrp7, X86Grps.opGrpNoSrc);
|
||||
},
|
||||
/**
|
||||
* @this {X86CPU}
|
||||
* opLAR()
|
||||
*
|
||||
* op=0x0F,0x02 (lar reg,rm)
|
||||
* op=0x0F,0x02 (LAR reg,rm)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
opLAR: function() {
|
||||
this.aOpModRegWord[this.getIPByte()].call(this, X86Help.opHelpLAR);
|
||||
},
|
||||
/**
|
||||
* @this {X86CPU}
|
||||
* opLSL()
|
||||
*
|
||||
* op=0x0F,0x03 (lsl reg,rm)
|
||||
* op=0x0F,0x03 (LSL reg,rm)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
opLSL: function() {
|
||||
this.aOpModRegWord[this.getIPByte()].call(this, X86Help.opHelpLSL);
|
||||
|
|
@ -82,6 +86,8 @@ var X86Op0F = {
|
|||
/**
|
||||
* opLOADALL()
|
||||
*
|
||||
* op=0x0F,0x05 (LOADALL)
|
||||
*
|
||||
* From the "Undocumented iAPX 286 Test Instruction" document at http://www.pcjs.org/pubs/pc/reference/intel/80286/loadall/:
|
||||
*
|
||||
* Physical Address (Hex) Associated CPU Register
|
||||
|
|
@ -117,8 +123,6 @@ var X86Op0F = {
|
|||
* no particular reason.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*
|
||||
* op=0x0F,0x05 (loadall)
|
||||
*/
|
||||
opLOADALL: function() {
|
||||
if (this.segCS.cpl) {
|
||||
|
|
@ -168,9 +172,11 @@ var X86Op0F = {
|
|||
if (DEBUG && DEBUGGER && (this.regCR0 & X86.CR0.MSW.PE)) this.stopCPU();
|
||||
},
|
||||
/**
|
||||
* @this {X86CPU}
|
||||
* opCLTS()
|
||||
*
|
||||
* op=0x0F,0x06 (clts)
|
||||
* op=0x0F,0x06 (CLTS)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
opCLTS: function() {
|
||||
if (this.segCS.cpl) {
|
||||
|
|
@ -181,6 +187,10 @@ var X86Op0F = {
|
|||
this.nStepCycles -= 2;
|
||||
},
|
||||
/**
|
||||
* opSLDT(dst, src)
|
||||
*
|
||||
* op=0x0F,0x00,reg=0x0 (GRP6:SLDT)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src (null)
|
||||
|
|
@ -191,6 +201,10 @@ var X86Op0F = {
|
|||
return this.segLDT.sel;
|
||||
},
|
||||
/**
|
||||
* opSTR(dst, src)
|
||||
*
|
||||
* op=0x0F,0x00,reg=0x1 (GRP6:STR)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src (null)
|
||||
|
|
@ -201,6 +215,10 @@ var X86Op0F = {
|
|||
return this.segTSS.sel;
|
||||
},
|
||||
/**
|
||||
* opLLDT(dst, src)
|
||||
*
|
||||
* op=0x0F,0x00,reg=0x2 (GRP6:LLDT)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src (null)
|
||||
|
|
@ -213,6 +231,10 @@ var X86Op0F = {
|
|||
return dst;
|
||||
},
|
||||
/**
|
||||
* opLTR(dst, src)
|
||||
*
|
||||
* op=0x0F,0x00,reg=0x3 (GRP6:LTR)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src (null)
|
||||
|
|
@ -228,6 +250,10 @@ var X86Op0F = {
|
|||
return dst;
|
||||
},
|
||||
/**
|
||||
* opVERR(dst, src)
|
||||
*
|
||||
* op=0x0F,0x00,reg=0x4 (GRP6:VERR)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src (null)
|
||||
|
|
@ -264,6 +290,10 @@ var X86Op0F = {
|
|||
return dst;
|
||||
},
|
||||
/**
|
||||
* opVERW(dst, src)
|
||||
*
|
||||
* op=0x0F,0x00,reg=0x5 (GRP6:VERW)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src (null)
|
||||
|
|
@ -295,6 +325,10 @@ var X86Op0F = {
|
|||
return dst;
|
||||
},
|
||||
/**
|
||||
* opSGDT(dst, src)
|
||||
*
|
||||
* op=0x0F,0x01,reg=0x0 (GRP7:SGDT)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src (null)
|
||||
|
|
@ -349,6 +383,10 @@ var X86Op0F = {
|
|||
return dst;
|
||||
},
|
||||
/**
|
||||
* opSIDT(dst, src)
|
||||
*
|
||||
* op=0x0F,0x01,reg=0x1 (GRP7:SIDT)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src (null)
|
||||
|
|
@ -378,6 +416,8 @@ var X86Op0F = {
|
|||
/**
|
||||
* opLGDT(dst, src)
|
||||
*
|
||||
* op=0x0F,0x01,reg=0x2 (GRP7:LGDT)
|
||||
*
|
||||
* The 80286 LGDT instruction expects a 40-bit operand: a 16-bit limit, followed by a 24-bit address;
|
||||
* the ModRM decoder has already supplied the first word of the operand (in dst), which corresponds to the
|
||||
* limit, so we must fetch the remaining 24 bits ourselves.
|
||||
|
|
@ -401,6 +441,8 @@ var X86Op0F = {
|
|||
/**
|
||||
* opLIDT(dst, src)
|
||||
*
|
||||
* op=0x0F,0x01,reg=0x3 (GRP7:LIDT)
|
||||
*
|
||||
* The 80286 LIDT instruction expects a 40-bit operand: a 16-bit limit, followed by a 24-bit address;
|
||||
* the ModRM decoder has already supplied the first word of the operand (in dst), which corresponds to the
|
||||
* limit, so we must fetch the remaining 24 bits ourselves.
|
||||
|
|
@ -422,6 +464,10 @@ var X86Op0F = {
|
|||
return dst;
|
||||
},
|
||||
/**
|
||||
* opSMSW(dst, src)
|
||||
*
|
||||
* op=0x0F,0x01,reg=0x4 (GRP7:SMSW)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src (null)
|
||||
|
|
@ -432,6 +478,10 @@ var X86Op0F = {
|
|||
return this.regCR0;
|
||||
},
|
||||
/**
|
||||
* opLMSW(dst, src)
|
||||
*
|
||||
* op=0x0F,0x01,reg=0x6 (GRP7:LMSW)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
* @param {number} src (null)
|
||||
|
|
@ -442,6 +492,118 @@ var X86Op0F = {
|
|||
this.nStepCycles -= (this.regEA < 0? 3 : 6);
|
||||
this.opFlags |= X86.OPFLAG.NOWRITE;
|
||||
return dst;
|
||||
},
|
||||
/**
|
||||
* opMOVrcr()
|
||||
*
|
||||
* op=0x0F,0x20 (MOV reg,cr)
|
||||
*
|
||||
* NOTE: Since the ModRM decoders deal only with general-purpose registers, we must move
|
||||
* the appropriate control register into a special variable (regMD16), which our helper function
|
||||
* (opHelpMOVMD16) will use to replace the decoder's src operand.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
opMOVrcr: function() {
|
||||
var bModRM = this.getIPByte() | 0xc0;
|
||||
/*
|
||||
* Unlike, say, opcode 0x8C (MOV word,sr), this opcode supports only registers, not memory;
|
||||
* however, the 80386 apparently ignores the mod bits, treating any combination as if it was 0xc0.
|
||||
*
|
||||
if ((bModRM & 0xc0) != 0xc0) {
|
||||
X86Help.opHelpInvalid.call(this);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
var reg = (bModRM & 0x38) >> 3;
|
||||
switch (reg) {
|
||||
case 0x0:
|
||||
this.regMD16 = this.regCR0;
|
||||
break;
|
||||
case 0x1:
|
||||
this.regMD16 = this.regCR1;
|
||||
break;
|
||||
case 0x2:
|
||||
this.regMD16 = this.regCR2;
|
||||
break;
|
||||
case 0x3:
|
||||
this.regMD16 = this.regCR3;
|
||||
break;
|
||||
default:
|
||||
X86Help.opHelpUndefined.call(this);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Like other MOV operations, the destination does not need to be read, just written;
|
||||
* however, it's moot, because we've already restricted this opcode to registers only.
|
||||
*
|
||||
* this.opFlags |= X86.OPFLAG.NOREAD;
|
||||
*/
|
||||
this.aOpModRegWord[bModRM].call(this, X86Help.opHelpMOVMD16);
|
||||
},
|
||||
/**
|
||||
* opMOVcrr()
|
||||
*
|
||||
* op=0x0F,0x22 (MOV cr,reg)
|
||||
*
|
||||
* NOTE: Since the ModRM decoders deal only with general-purpose registers, we have to
|
||||
* make a note of which general-purpose register will be overwritten, so that we can restore it
|
||||
* after moving the modified value to the correct control register.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
opMOVcrr: function() {
|
||||
var temp;
|
||||
var bModRM = this.getIPByte() | 0xc0;
|
||||
/*
|
||||
* Unlike, say, opcode 0x8E (MOV sr,word), this opcode supports only registers, not memory;
|
||||
* however, the 80386 apparently ignores the mod bits, treating any combination as if it was 0xc0.
|
||||
*
|
||||
if ((bModRM & 0xc0) != 0xc0) {
|
||||
X86Help.opHelpInvalid.call(this);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
var reg = (bModRM & 0x38) >> 3;
|
||||
switch(reg) {
|
||||
case 0x0:
|
||||
temp = this.regEAX;
|
||||
break;
|
||||
case 0x1:
|
||||
temp = this.regECX; // TODO: Is setting CR1 actually allowed on an 80386?
|
||||
break;
|
||||
case 0x2:
|
||||
temp = this.regEDX;
|
||||
break;
|
||||
case 0x3:
|
||||
temp = this.regEBX;
|
||||
break;
|
||||
default:
|
||||
X86Help.opHelpInvalid.call(this);
|
||||
return;
|
||||
}
|
||||
this.aOpModRegWord[bModRM].call(this, X86Help.opHelpMOV);
|
||||
switch (reg) {
|
||||
case 0x0:
|
||||
reg = this.regEAX;
|
||||
this.regEAX = temp;
|
||||
X86Help.opHelpLCR0.call(this, reg);
|
||||
break;
|
||||
case 0x1:
|
||||
this.regCR1 = this.regECX;
|
||||
this.regECX = temp;
|
||||
break;
|
||||
case 0x2:
|
||||
this.regCR2 = this.regEDX;
|
||||
this.regEDX = temp;
|
||||
break;
|
||||
case 0x3:
|
||||
this.regCR3 = this.regEBX;
|
||||
this.regEBX = temp;
|
||||
break;
|
||||
default:
|
||||
break; // there IS no other case, but JavaScript inspections don't know that
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ var X86OpXX = {
|
|||
* @this {X86CPU}
|
||||
*/
|
||||
op0F: function() {
|
||||
X86Op0F.aOps0F[this.getIPByte()].call(this);
|
||||
this.aOps0F[this.getIPByte()].call(this);
|
||||
},
|
||||
/**
|
||||
* op=0x10 (ADC byte,reg)
|
||||
|
|
@ -1327,6 +1327,36 @@ var X86OpXX = {
|
|||
opARPL: function() {
|
||||
this.aOpModMemWord[this.getIPByte()].call(this, X86Help.opHelpARPL);
|
||||
},
|
||||
/**
|
||||
* op=0x64 (FS:)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
opFS: function() {
|
||||
/*
|
||||
* NOTE: The fact that we're setting NOINTR along with SEG is really just for documentation purposes;
|
||||
* the way stepCPU() is written, the presence of any prefix bypasses normal interrupt processing anyway.
|
||||
*/
|
||||
this.opFlags |= X86.OPFLAG.SEG | X86.OPFLAG.NOINTR;
|
||||
this.segData = this.segStack = this.segFS;
|
||||
this.nStepCycles -= this.CYCLES.nOpCyclesPrefix;
|
||||
this.stopCPU();
|
||||
},
|
||||
/**
|
||||
* op=0x65 (GS:)
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
opGS: function() {
|
||||
/*
|
||||
* NOTE: The fact that we're setting NOINTR along with SEG is really just for documentation purposes;
|
||||
* the way stepCPU() is written, the presence of any prefix bypasses normal interrupt processing anyway.
|
||||
*/
|
||||
this.opFlags |= X86.OPFLAG.SEG | X86.OPFLAG.NOINTR;
|
||||
this.segData = this.segStack = this.segGS;
|
||||
this.nStepCycles -= this.CYCLES.nOpCyclesPrefix;
|
||||
this.stopCPU();
|
||||
},
|
||||
/**
|
||||
* op=0x66 (OS:) (80386 and up)
|
||||
*
|
||||
|
|
@ -1339,8 +1369,9 @@ var X86OpXX = {
|
|||
this.opFlags |= X86.OPFLAG.SEG;
|
||||
this.dataSize ^= 0x6; // that which is 2 shall become 4, and vice versa
|
||||
this.dataMask ^= (0xffff0000|0); // that which is 0x0000ffff shall become 0xffffffff, and vice versa
|
||||
this.opMem = this.aaOpMem[this.dataSize];
|
||||
this.setDataSize();
|
||||
this.nStepCycles -= this.CYCLES.nOpCyclesPrefix;
|
||||
this.stopCPU();
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
|
@ -1355,8 +1386,9 @@ var X86OpXX = {
|
|||
this.opFlags |= X86.OPFLAG.SEG;
|
||||
this.addrSize ^= 0x06; // that which is 2 shall become 4, and vice versa
|
||||
this.addrMask ^= (0xffff0000|0); // that which is 0x0000ffff shall become 0xffffffff, and vice versa
|
||||
this.setOpMod();
|
||||
this.setAddrSize();
|
||||
this.nStepCycles -= this.CYCLES.nOpCyclesPrefix;
|
||||
this.stopCPU();
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
|
@ -1929,7 +1961,7 @@ var X86OpXX = {
|
|||
*
|
||||
* NOTE: Since the ModRM decoders deal only with general-purpose registers, we must move
|
||||
* the appropriate segment register into a special variable (regMD16), which our helper function
|
||||
* (opHelpMOVSegSrc) will use to replace the decoder's src operand.
|
||||
* (opHelpMOVMD16) will use to replace the decoder's src operand.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
|
|
@ -1957,7 +1989,7 @@ var X86OpXX = {
|
|||
* Like other MOV operations, the destination does not need to be read, just written.
|
||||
*/
|
||||
this.opFlags |= X86.OPFLAG.NOREAD;
|
||||
this.aOpModMemWord[bModRM].call(this, X86Help.opHelpMOVSegSrc);
|
||||
this.aOpModMemWord[bModRM].call(this, X86Help.opHelpMOVMD16);
|
||||
},
|
||||
/**
|
||||
* op=0x8D (LEA reg,word)
|
||||
|
|
|
|||
|
|
@ -569,6 +569,12 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
|
|||
var ext = cpu.getShort(addrDesc + X86.DESC.EXT.OFFSET);
|
||||
var selMasked = sel & X86.SEL.MASK;
|
||||
|
||||
if (I386 && cpu.model >= X86.MODEL_80386) {
|
||||
base |= (ext & X86.DESC.EXT.BASE2431) << 16;
|
||||
limit |= (ext & X86.DESC.EXT.LIMIT1619) << 16;
|
||||
if (ext & X86.DESC.EXT.GRANULARITY) limit = (limit << 12) | 0xfff;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
|
||||
var selCode, cplPrev, addrTSS, offSP, offSS, regSPPrev, regSSPrev;
|
||||
|
|
@ -863,11 +869,11 @@ X86Seg.prototype.updateMode = function(fProt)
|
|||
this.cpl = this.sel & X86.SEL.RPL;
|
||||
this.dpl = (this.acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT;
|
||||
if (this.cpu.model < X86.MODEL_80386 || !(this.ext & X86.DESC.EXT.BIG)) {
|
||||
this.dataSize = 2;
|
||||
this.dataMask = 0xffff;
|
||||
this.addrSize = 2;
|
||||
this.addrMask = 0xffff;
|
||||
} else {
|
||||
this.dataSize = 4;
|
||||
this.dataMask = (0xffffffff|0);
|
||||
this.addrSize = 4;
|
||||
this.addrMask = (0xffffffff|0);
|
||||
}
|
||||
} else {
|
||||
this.load = X86Seg.loadReal;
|
||||
|
|
@ -877,11 +883,11 @@ X86Seg.prototype.updateMode = function(fProt)
|
|||
this.limit = 0xffff;
|
||||
this.cpl = this.dpl = 0;
|
||||
this.addrDesc = X86.ADDR_INVALID;
|
||||
this.dataSize = 2;
|
||||
this.dataMask = 0xffff;
|
||||
this.addrSize = 2;
|
||||
this.addrMask = 0xffff;
|
||||
}
|
||||
this.addrSize = this.dataSize;
|
||||
this.addrMask = this.dataMask;
|
||||
this.dataSize = this.addrSize;
|
||||
this.dataMask = this.addrMask;
|
||||
return fProt;
|
||||
};
|
||||
|
||||
|
|
@ -904,7 +910,7 @@ X86Seg.prototype.messageSeg = function(sel, base, limit, acc, ext)
|
|||
if (this.id == X86Seg.ID.CODE) sDPL += " cpl=" + this.cpl;
|
||||
this.dbg.message("loadSeg(" + this.sName + "):" + ch + "sel=" + str.toHexWord(sel) + " base=" + str.toHex(base) + " limit=" + str.toHexWord(limit) + " acc=" + str.toHexWord(acc) + sDPL);
|
||||
}
|
||||
this.cpu.assert(/* base != X86.ADDR_INVALID && */ (!ext || ext == X86.DESC.EXT.AVAIL));
|
||||
this.cpu.assert(/* base != X86.ADDR_INVALID && */ (this.cpu.model >= X86.MODEL_80386 || !ext || ext == X86.DESC.EXT.AVAIL));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ str.parseInt = function(s, base)
|
|||
}
|
||||
var v;
|
||||
if (str.isValidInt(s, base) && !isNaN(v = parseInt(s, base))) {
|
||||
value = v;
|
||||
value = v|0;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
|
|
|
|||
Loading…
Reference in a new issue