More 80386 support (PUSH/POP FS/GS)

This commit is contained in:
Jeff Parsons 2015-03-27 00:14:46 -07:00 committed by jeffpar
commit e4b5f2b020
4 changed files with 117 additions and 32 deletions

View file

@ -27,13 +27,13 @@ just ignore it.
Property names with all UPPER-CASE letters (with optional numbers and/or underscores) represent constants.
I originally adopted this rule in part because it's a popular C language convention, but also because it
made it easy to write a preprocessing script (see the PCjs Grunt task "prepjs") that replaced all such property
references with the corresponding property values and then removed the original property definitions.
Of course, this convention also depended on the properties never being modified *or* enumerated.
made it easy to write a preprocessing script (see the PCjs Grunt task [prepjs](/modules/grunts/prepjs/))
that replaced all such property references with the corresponding property values and then removed the original
property definitions. Of course, this convention also depended on the properties never being modified *or* enumerated.
I later discovered that Google's Closure Compiler does an excellent job of automatically inlining properties
that are never modified or enumerated, so the "prepjs" preprocessing script is no longer used, but I've stuck
with the UPPER-CASE convention.
that are never modified or enumerated, so the [prepjs](/modules/grunts/prepjs/) preprocessing script is no longer used,
but I've stuck with the UPPER-CASE convention.
I don't bother with JSDoc *@const* annotations, because 1) the project contains far too many constants, 2)
all the constants are already effectively annotated by virtue of being UPPER-CASE, and 3) there is no noticeable
@ -54,7 +54,7 @@ but since the EOI command is actually one of a number Operation Command Words (s
ChipSet.OCW2_EOI = 0x20; // non-specific EOI (end-of-interrupt)
and since I also like to group constants that are associated with a particular register or port, and since I don't
want the ChipSet constructor becoming littered with property constants, I first define a "constant object"; in this
want the ChipSet constructor becoming littered with property constants, I first define a constant object; in this
case, **PIC_LO**:
ChipSet.PIC_LO = {};
@ -63,8 +63,10 @@ case, **PIC_LO**:
ChipSet.PIC_LO.OCW2_EOI_ROT = 0xA0; // rotate on non-specific EOI
ChipSet.PIC_LO.OCW2_EOI_ROTSPEC = 0xE0; // rotate on specific EOI
Also, by defining constants using the "long form" above, rather than the more conventional "short form" (ie, standard
Object notation):
By using fully-qualified property names for each constant, the code has a more C-like appearance (think *#define*)
that's also easier to preprocess.
However, I've gradually switched to the more conventional JavaScript object notation for class constants:
ChipSet.PIC_LO = {
OCW2_EOI: 0x20, // non-specific EOI (end-of-interrupt)
@ -73,10 +75,8 @@ Object notation):
OCW2_EOI_ROTSPEC: 0xE0 // rotate on specific EOI
};
the code follows a more traditional, C-like style (think *#define*). It's also easier to preprocess code that
uses the "long form." But again, since the Closure Compiler already does a good job of inlining, it's no longer
necessary to use the "long form", which is why you'll see newer code using more conventional Object notation to
define class constants.
because, again, the Closure Compiler does an excellent job inlining such constants (or indeed any property that is
never modified *or* enumerated).
### DEBUG vs. RELEASE

View file

@ -1,6 +1,6 @@
JavaScript Idiosyncrasies
---
Time to mention a few JavaScript idiosyncrasies that newcomers may not be aware of, and how I deal with them.
Time to mention a few JavaScript idiosyncrasies, 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/).
@ -12,17 +12,17 @@ ways. We can thank the early days of JavaScript for this feature, when it was t
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 "!==").
To avoid unexpected type coercion, and thus unexpected matches and/or mismatches, the usual advice is to *always* use
strict equality operators ("===" 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, the fewer errors you'll have. And type 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:
be omitted or set to *null*. Using "==", you can check for either value with a single comparison:
if (parameter == null) { ... }
@ -30,11 +30,31 @@ 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.
This is one of the few times I think coercion (of *undefined* to *null*) is beneficial.
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.
Another common pattern:
if (!b) { ... }
is a popular way of checking for "falsy" values (ie, *undefined*, *null*, 0, false, "", NaN, etc).
Again, another situation where type coercion is beneficial and well understood. Don't use this technique for
the *undefined* or *null* parameter however:
if (!parameter) { ... }
because a valid numeric parameter could include 0, a valid string parameter could include "", etc.
When I recommend *not* using strict comparisons, I'm not saying coercion is good. I agree that it generally
should be avoided, except in well-defined situations, as noted above. Know your variable data types, compare
variables only of the same type, and you'll be fine.
Problems with type coercion are **NOT** problems caused by a poor choice of operators, so trying to make
those problems go away by artificially limiting your choice of operators is the wrong solution. Type coercion
problems are, by definition, problems involving mismatched types. Solutions include:
- Don't compare variables of different types; or
- Manually convert your variables to matching types; or
- Allow JavaScript to perform coercion only in well-defined situations
### Enumerating Array or Object Properties
@ -55,7 +75,7 @@ However, a more elegant solution is to use the unary "+" operator to coerce the
+i;
### Shift Counts For Bit-wise Shifts
### Shift Counts For Bitwise 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:
@ -71,22 +91,23 @@ 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:
shift count is in the range 0-31. For example, here's how you could shift a number by 32 bits:
n = (n >>> 31) >>> 1;
Also, it's not quite correct to say that a shift count of zero has *no* effect on a value:
Also, it's not quite correct to say that a shift count of zero has *no* effect on a number:
n = 0x88888888|0; // n is displayed as -2004318072
n >>>= 0; // n is displayed as 2290649224
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.
It's true that the bottom 32 bits of the number 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:
I consider this an anomaly of JavaScript's bitwise operators, because it breaks the "rule" that bitwise operators
operate *only* on the low 32 bits of a number. And as soon as you perform any other bitwise operation on the number,
even one that does not modify the low 32 bits, the upper bits will revert to the sign of the lower 32-bit value:
n |= 0; // n is displayed as -2004318072 again
n |= 0; // n is displayed as -2004318072 again
*[@jeffpar](http://twitter.com/jeffpar)*
*March 26, 2015*

View file

@ -879,7 +879,11 @@ if (DEBUGGER) {
0x05: [Debugger.INS.LOADALL,Debugger.TYPE_80286],
0x06: [Debugger.INS.CLTS, Debugger.TYPE_80286],
0x20: [Debugger.INS.MOV, Debugger.TYPE_REG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_CTLREG | Debugger.TYPE_DWORD | Debugger.TYPE_IN],
0x22: [Debugger.INS.MOV, Debugger.TYPE_CTLREG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_DWORD | Debugger.TYPE_IN]
0x22: [Debugger.INS.MOV, Debugger.TYPE_CTLREG | Debugger.TYPE_DWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_DWORD | Debugger.TYPE_IN],
0xA0: [Debugger.INS.PUSH, Debugger.TYPE_FS | Debugger.TYPE_IN | Debugger.TYPE_80386],
0xA1: [Debugger.INS.POP, Debugger.TYPE_FS | Debugger.TYPE_OUT | Debugger.TYPE_80386],
0xA8: [Debugger.INS.PUSH, Debugger.TYPE_GS | Debugger.TYPE_IN | Debugger.TYPE_80386],
0xA9: [Debugger.INS.POP, Debugger.TYPE_GS | Debugger.TYPE_OUT | Debugger.TYPE_80386]
};
Debugger.aaGrpDescs = [
@ -4062,6 +4066,10 @@ if (DEBUGGER) {
return;
for (var i = 2; i < asArgs.length; i++) {
var b = str.parseInt(asArgs[i], 16);
if (b === undefined) {
this.println("unrecognized value: " + str.toHexByte(b));
break;
}
this.println("setting " + this.hexAddr(aAddr) + " to " + str.toHexByte(b));
this.setByte(aAddr, b, 1);
}

View file

@ -311,6 +311,58 @@ X86.opMOVcrr = function MOVcrr()
}
};
/**
* opPUSHFS()
*
* op=0x0F,0xA0 (PUSH FS)
*
* @this {X86CPU}
*/
X86.opPUSHFS = function PUSHFS()
{
this.pushWord(this.segFS.sel);
this.nStepCycles -= this.CYCLES.nOpCyclesPushSeg;
};
/**
* opPOPFS()
*
* op=0x0F,0xA1 (POP FS)
*
* @this {X86CPU}
*/
X86.opPOPFS = function POPFS()
{
this.setFS(this.popWord());
this.nStepCycles -= this.CYCLES.nOpCyclesPopReg;
};
/**
* opPUSHGS()
*
* op=0x0F,0xA8 (PUSH GS)
*
* @this {X86CPU}
*/
X86.opPUSHGS = function PUSHGS()
{
this.pushWord(this.segGS.sel);
this.nStepCycles -= this.CYCLES.nOpCyclesPushSeg;
};
/**
* opPOPGS()
*
* op=0x0F,0xA9 (POP GS)
*
* @this {X86CPU}
*/
X86.opPOPGS = function POPGS()
{
this.setGS(this.popWord());
this.nStepCycles -= this.CYCLES.nOpCyclesPopReg;
};
X86.aOps0F = new Array(256);
X86.aOps0F[0x00] = X86.opGrp6;
@ -334,6 +386,10 @@ if (I386) {
X86.aOps0F386 = [];
X86.aOps0F386[0x20] = X86.opMOVrcr;
X86.aOps0F386[0x22] = X86.opMOVcrr;
X86.aOps0F386[0xA0] = X86.opPUSHFS;
X86.aOps0F386[0xA1] = X86.opPOPFS;
X86.aOps0F386[0xA8] = X86.opPUSHGS;
X86.aOps0F386[0xA9] = X86.opPOPGS;
}
/*