Change how optional opcode tables are built

This commit is contained in:
Jeff Parsons 2015-03-26 16:56:37 -07:00 committed by jeffpar
commit e3a5408031
10 changed files with 145 additions and 104 deletions

92
blog/2015/03/26/README.md Normal file
View file

@ -0,0 +1,92 @@
JavaScript Idiosyncrasies
---
Time to mention a few JavaScript idiosyncrasies that newcomers may not be aware of, and how I deal with them.
Also, see my previous posts on [PCjs Coding Conventions](/blog/2014/09/30/) and [JavaScript Negativity](/blog/2014/10/26/).
### Strict Equality
Most sites will advise you to *never* use the "==" and "!=" JavaScript operators, because when they compare variables
containing different data types, JavaScript will coerce one of the operands to a matching type, sometimes in unexpected
ways. We can thank the early days of JavaScript for this feature, when it was trying to be extraordinarily forgiving
of sloppy code. I'm not going to list all the odd results that can arise from JavaScript's operand coercion, because
there are more than enough examples on the web already.
To avoid unexpected coercion, and thus unexpected matches and/or mismatches, the usual advice is to *always* use
strict equality operators instead ("===" and "!==").
I disagree. In properly written code, you should always know what type of data your variables contain. In fact,
the more you're able to use JSDoc types to declare the data types of all your parameters, return values, and other
variables, the fewer errors you'll have. And coercion will never be a problem as long as you're always comparing
variables with matching types, because no coercion will be performed.
Another problem with strict equality operators is that they require more work to check for both *undefined* and *null*
values. For example, when I write a method with optional parameters, I generally allow those parameters to either
be omitted or set to *null*. Using "==", you can check both cases with a single comparison:
if (parameter == null) { ... }
whereas strict equality requires more work:
if (parameter === undefined || parameter === null) { ... }
This is one of the few times I think coercion (of *undefined* to *null*) is beneficial, so I rely on it.
When I recommend that you *not* use strict comparisons, I'm not saying that coercion is good. I agree that it
generally should be avoided (except in situations like the last example). The point is, know your variable data
types, only compare variables of the same type, and you'll never have to worry about coercion.
### Enumerating Array or Object Properties
When using *for*...*in* loops like this:
var a = [100, 200, 300];
for (var i in a) { ... }
the type of variable *i* will be **string** rather than **number**; that is, it will contain "0", "1" and "2" rather
than 0, 1 and 2. If you then use *i* to set a matching element in another array, that element will not be stored in
the same (numeric) position as the original array.
One solution is to convert *i* to a **number**:
parseInt(i, 10);
However, a more elegant solution is to use the unary "+" operator to coerce the **string** to a **number**:
+i;
### Shift Counts For Bit-wise Shifts
It turns out that shifting an integer value by more than 31 bits in either direction may not shift as many bits as
you'd expect. For example:
n = 0x10000000;
n >>>= 32;
will not change n at all. This is because, just like the shift instructions on Intel processors, JavaScript converts
the shift count to a *mod 32* value (in other words, it truncates the shift count to a 5-bit value).
So the above example is equivalent to:
n >>>= 0;
If you really need larger shift counts to work in a consistent manner, you can perform multiple shifts, where each
shift count is in the range 0-31:
n = (n >>> 31) >>> 1;
Also, it's not quite correct to say that a shift count of zero has *no* effect on a value:
n = 0x88888888|0; // n is displayed as -2004318072
n >>>= 0; // n is displayed as 2290649224
It's true that the bottom 32 bits of the value were not changed, but a side-effect of the unsigned shift operator is
that all the upper sign bits are stripped from the (64-bit) result.
However, as soon as you perform another bit-wise operation on the value, even one that has no effect on the lower 32
bits, the upper bits will once be updated to match the sign of the lower 32-bit value:
n |= 0; // n is displayed as -2004318072 again
*[@jeffpar](http://twitter.com/jeffpar)*
*March 26, 2015*

View file

@ -1316,11 +1316,7 @@ Bus.prototype.addPortInputTable = function(component, table, offset)
{
if (offset === undefined) offset = 0;
for (var port in table) {
/*
* JavaScript coerces property keys to strings, so we use parseInt() to coerce them back to numbers.
*/
port = parseInt(port, 10);
this.addPortInputNotify(port + offset, port + offset, component, table[port]);
this.addPortInputNotify(+port + offset, +port + offset, component, table[port]);
}
};
@ -1440,11 +1436,7 @@ Bus.prototype.addPortOutputTable = function(component, table, offset)
{
if (offset === undefined) offset = 0;
for (var port in table) {
/*
* JavaScript converts property keys to strings (brilliant), so we use parseInt() to convert them back to numbers.
*/
port = parseInt(port, 10);
this.addPortOutputNotify(port + offset, port + offset, component, table[port]);
this.addPortOutputNotify(+port + offset, +port + offset, component, table[port]);
}
};

View file

@ -2187,7 +2187,7 @@ ChipSet.prototype.toggleSwitch = function(control)
this.setSwitch(control, f);
var sID = control.getAttribute("id");
var asParts = sID.split("-");
var b = (0x1 << (parseInt(asParts[1], 10) - 1));
var b = (0x1 << (+asParts[1] - 1));
switch (asParts[0]) {
case "sw1":
this.sw1Init = (this.sw1Init & ~b) | (f? 0 : b);
@ -4795,13 +4795,16 @@ ChipSet.prototype.intBIOSRTC = function(addr)
* parseSwitches(s, def)
*
* @this {ChipSet}
* @param {string|undefined} s describing switch settings (can't simply use parseInt() with a base of 2, because the bit order is reversed, as well as the bit sense)
* @param {string|undefined} s describing switch settings
* @param {number} def is a default value to use if s is undefined
* @return {number} value representing the switch settings
*/
ChipSet.prototype.parseSwitches = function(s, def)
{
if (s === undefined) return def;
/*
* NOTE: We can't simply use parseInt() with a base of 2, because the bit order is reversed, as well as the bit sense.
*/
var b = 0, bit = 0x1;
for (var i = 0; i < s.length; i++) {
if (s.charAt(i) == "0") b |= bit;

View file

@ -187,10 +187,10 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
var sStatePath = null;
var sResume = parmsComputer['resume'];
if (sResume !== undefined) {
if (sResume.length > 1) {
sStatePath = this.sResumePath = sResume;
if (sResume.length == 1) {
this.resume = +sResume;
} else {
this.resume = parseInt(sResume, 10);
sStatePath = this.sResumePath = sResume;
}
}

View file

@ -4015,7 +4015,7 @@ if (DEBUGGER) {
var fWords = (sCmd == "dw");
if (sLen !== undefined) {
if (sLen.charAt(0) == "l") sLen = sLen.substr(1);
cLines = parseInt(sLen, 10);
cLines = +sLen;
if (cLines) cLines = fWords? ((cLines + 7) >> 3) : ((cLines + 15) >> 4);
}
if (!cLines) cLines = 8;
@ -4061,7 +4061,7 @@ if (DEBUGGER) {
if (aAddr[0] == null)
return;
for (var i = 2; i < asArgs.length; i++) {
var b = parseInt(asArgs[i], 16);
var b = str.parseInt(asArgs[i], 16);
this.println("setting " + this.hexAddr(aAddr) + " to " + str.toHexByte(b));
this.setByte(aAddr, b, 1);
}
@ -4133,7 +4133,7 @@ if (DEBUGGER) {
var iHistory = this.iOpcodeHistory;
var aHistory = this.aOpcodeHistory;
if (aHistory.length) {
var n = (sCount === undefined? this.nextHistory : parseInt(sCount, 10));
var n = (sCount === undefined? this.nextHistory : +sCount);
if (isNaN(n))
n = cLines;
else
@ -4203,7 +4203,7 @@ if (DEBUGGER) {
var fPrint = false;
if (sCategory == "DUMP") {
var sDump = "";
var cLines = (sEnable === undefined? -1 : parseInt(sEnable, 10));
var cLines = (sEnable === undefined? -1 : +sEnable);
var i = this.iTraceBuffer;
do {
var s = this.aTraceBuffer[i++];
@ -4509,9 +4509,7 @@ if (DEBUGGER) {
switch (asArgs[1]) {
case "cs":
var nCycles;
if (asArgs[3] !== undefined) {
nCycles = parseInt(asArgs[3], 10);
}
if (asArgs[3] !== undefined) nCycles = +asArgs[3];
switch (asArgs[2]) {
case "int":
this.cpu.aCounts.nCyclesChecksumInterval = nCycles;
@ -4533,7 +4531,7 @@ if (DEBUGGER) {
break;
case "sp":
if (asArgs[2] !== undefined) {
this.cpu.setSpeed(parseInt(asArgs[2], 10));
this.cpu.setSpeed(+asArgs[2]);
}
this.println("target speed: " + this.cpu.getSpeedTarget() + " (" + this.cpu.getSpeed() + "x)");
break;
@ -4610,7 +4608,7 @@ if (DEBUGGER) {
this.println("missing value for " + asArgs[1]);
return;
}
var w = parseInt(sValue, 16);
var w = str.parseInt(sValue, 16);
if (!isNaN(w)) {
var sRegMatch = sReg.toUpperCase();
if (sRegMatch.charAt(0) == 'E' && this.cchReg <= 4) {
@ -4938,7 +4936,7 @@ if (DEBUGGER) {
{
var dbg = this;
var fRegs = (sCmd == "tr");
var count = (sCount != null? parseInt(sCount, 10) : 1);
var count = (sCount != null? +sCount : 1);
var nCycles = (count == 1? 0 : 1);
web.onCountRepeat(
count,

View file

@ -1194,7 +1194,7 @@ FDC.prototype.loadSelectedDrive = function(sDisketteName, sDiskettePath, file)
{
var iDrive;
var controlDrives = this.bindings["listDrives"];
if (controlDrives && !isNaN(iDrive = parseInt(controlDrives.value, 10)) && iDrive >= 0 && iDrive < this.aDrives.length) {
if (controlDrives && !isNaN(iDrive = str.parseInt(controlDrives.value, 10)) && iDrive >= 0 && iDrive < this.aDrives.length) {
if (!sDiskettePath) {
this.unloadDrive(iDrive);
@ -1437,7 +1437,7 @@ FDC.prototype.displayDiskette = function(iDrive, fUpdateDrive)
* Next, make sure the drive whose disk we're updating is the currently selected drive.
*/
var i;
var iDriveSelected = parseInt(controlDrives.value, 10);
var iDriveSelected = str.parseInt(controlDrives.value, 10);
var sTargetPath = (drive.fLocal? "?" : drive.sDiskettePath);
if (!isNaN(iDriveSelected) && iDriveSelected == iDrive) {
for (i = 0; i < controlDisks.options.length; i++) {
@ -1452,7 +1452,7 @@ FDC.prototype.displayDiskette = function(iDrive, fUpdateDrive)
}
if (fUpdateDrive) {
for (i = 0; i < controlDrives.options.length; i++) {
if (parseInt(controlDrives.options[i].value, 10) == drive.iDrive) {
if (str.parseInt(controlDrives.options[i].value, 10) == drive.iDrive) {
if (controlDrives.selectedIndex != i) {
controlDrives.selectedIndex = i;
}

View file

@ -1090,7 +1090,7 @@ Keyboard.prototype.findBinding = function(simCode, sType, fDown)
if (this.cSoftCodes) {
for (var code in Keyboard.SHIFTED_KEYCODES) {
if (simCode == Keyboard.SHIFTED_KEYCODES[code]) {
simCode = parseInt(code, 10);
simCode = +code;
code = Keyboard.STUPID_KEYCODES[code];
if (code) simCode = code;
break;

View file

@ -256,7 +256,7 @@ ROM.prototype.onLoadROM = function(sROMFile, sROMData, nErrorCode)
var asHexData = sHexData.split(" ");
this.abROM = new Array(asHexData.length);
for (var i = 0; i < asHexData.length; i++) {
this.abROM[i] = parseInt(asHexData[i], 16);
this.abROM[i] = str.parseInt(asHexData[i], 16);
}
}
this.copyROM();

View file

@ -761,13 +761,15 @@ X86CPU.prototype.initProcessor = function()
this.aOps[X86.OPCODE.PUSHSP] = X86.opPUSHSP;
if (I386 && this.model >= X86.MODEL_80386) {
var bOpcode;
this.aOps[X86.OPCODE.FS] = X86.opFS;
this.aOps[X86.OPCODE.GS] = X86.opGS;
this.aOps[X86.OPCODE.OS] = X86.opOS;
this.aOps[X86.OPCODE.AS] = X86.opAS;
this.aOps0F = X86.aOps0F.slice();
this.aOps0F[0x20] = X86.opMOVrcr;
this.aOps0F[0x22] = X86.opMOVcrr;
for (bOpcode in X86.aOps0F386) {
this.aOps0F[+bOpcode] = X86.aOps0F386[bOpcode];
}
/*
* Extend the opcode table by creating a mirror of the first 256 opcodes, but with dword-based
* opcode handlers (as defined in aOpsD) instead word-based opcode handlers. Whenever dataSize
@ -775,8 +777,8 @@ X86CPU.prototype.initProcessor = function()
* bOpcodeBias from 0 to 256.
*/
this.aOps = this.aOps.concat(this.aOps);
for (var bOpcode in X86.aOpsD) {
this.aOps[parseInt(bOpcode, 10) + 256] = X86.aOpsD[bOpcode];
for (bOpcode in X86.aOpsD) {
this.aOps[+bOpcode + 256] = X86.aOpsD[bOpcode];
}
}
}

View file

@ -311,76 +311,30 @@ X86.opMOVcrr = function MOVcrr()
}
};
X86.aOps0F = [
X86.opGrp6, X86.opGrp7, X86.opLAR, X86.opLSL, // 0x00-0x03
X86.opUndefined, X86.opLOADALL, X86.opCLTS, X86.opUndefined, // 0x04-0x07
/*
* On all processors (except the 8086/8088, of course), 0x0F,0x0B is also referred to as "UD2": an
* instruction guaranteed to raise a #UD (Invalid Opcode) exception (INT 0x06) on all future x86 processors.
*/
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opInvalid, // 0x08-0x0B
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x0C-0x0F
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x10-0x13
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x14-0x17
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x18-0x1B
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x1C-0x1F
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x20-0x23
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x24-0x27
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x28-0x2B
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x2C-0x2F
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x30-0x33
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x34-0x37
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x38-0x3B
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x3C-0x3F
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x40-0x43
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x44-0x47
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x48-0x4B
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x4C-0x4F
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x50-0x53
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x54-0x57
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x58-0x5B
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x5C-0x5F
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x60-0x63
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x64-0x67
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x68-0x6B
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x6C-0x6F
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x70-0x73
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x74-0x77
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x78-0x7B
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x7C-0x7F
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x80-0x83
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x84-0x87
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x88-0x8B
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x8C-0x8F
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x90-0x93
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x94-0x97
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x98-0x9B
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0x9C-0x9F
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xA0-0xA3
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xA4-0xA7
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xA8-0xAB
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xAC-0xAF
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xB0-0xB3
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xB4-0xB7
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xB8-0xBB
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xBC-0xBF
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xC0-0xC3
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xC4-0xC7
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xC8-0xCB
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xCC-0xCF
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xD0-0xD3
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xD4-0xD7
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xD8-0xDB
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xDC-0xDF
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xE0-0xE3
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xE4-0xE7
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xE8-0xEB
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xEC-0xEF
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xF0-0xF3
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xF4-0xF7
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined, // 0xF8-0xFB
X86.opUndefined, X86.opUndefined, X86.opUndefined, X86.opUndefined // 0xFC-0xFF
];
X86.aOps0F = new Array(256);
X86.aOps0F[0x00] = X86.opGrp6;
X86.aOps0F[0x01] = X86.opGrp7;
X86.aOps0F[0x02] = X86.opLAR;
X86.aOps0F[0x03] = X86.opLSL;
X86.aOps0F[0x05] = X86.opLOADALL;
X86.aOps0F[0x06] = X86.opCLTS;
/*
* On all processors (except the 8086/8088, of course), 0x0F,0x0B is also referred to as "UD2": an
* instruction guaranteed to raise a #UD (Invalid Opcode) exception (INT 0x06) on all future x86 processors.
*/
X86.aOps0F[0x0B] = X86.opInvalid;
for (var i = 0; i < X86.aOps0F.length; i++) {
if (!X86.aOps0F[i]) X86.aOps0F[i] = X86.opUndefined;
}
if (I386) {
X86.aOps0F386 = [];
X86.aOps0F386[0x20] = X86.opMOVrcr;
X86.aOps0F386[0x22] = X86.opMOVcrr;
}
/*
* These instruction groups are not as orthogonal as the original 8086/8088 groups (Grp1 through Grp4): some of