More 80386 support (PUSH/POP FS/GS)
This commit is contained in:
parent
e3a5408031
commit
e4b5f2b020
4 changed files with 117 additions and 32 deletions
|
|
@ -27,13 +27,13 @@ just ignore it.
|
||||||
Property names with all UPPER-CASE letters (with optional numbers and/or underscores) represent constants.
|
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
|
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
|
made it easy to write a preprocessing script (see the PCjs Grunt task [prepjs](/modules/grunts/prepjs/))
|
||||||
references with the corresponding property values and then removed the original property definitions.
|
that replaced all such property references with the corresponding property values and then removed the original
|
||||||
Of course, this convention also depended on the properties never being modified *or* enumerated.
|
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
|
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
|
that are never modified or enumerated, so the [prepjs](/modules/grunts/prepjs/) preprocessing script is no longer used,
|
||||||
with the UPPER-CASE convention.
|
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)
|
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
|
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)
|
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
|
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**:
|
case, **PIC_LO**:
|
||||||
|
|
||||||
ChipSet.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_ROT = 0xA0; // rotate on non-specific EOI
|
||||||
ChipSet.PIC_LO.OCW2_EOI_ROTSPEC = 0xE0; // rotate on 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
|
By using fully-qualified property names for each constant, the code has a more C-like appearance (think *#define*)
|
||||||
Object notation):
|
that's also easier to preprocess.
|
||||||
|
|
||||||
|
However, I've gradually switched to the more conventional JavaScript object notation for class constants:
|
||||||
|
|
||||||
ChipSet.PIC_LO = {
|
ChipSet.PIC_LO = {
|
||||||
OCW2_EOI: 0x20, // non-specific EOI (end-of-interrupt)
|
OCW2_EOI: 0x20, // non-specific EOI (end-of-interrupt)
|
||||||
|
|
@ -73,10 +75,8 @@ Object notation):
|
||||||
OCW2_EOI_ROTSPEC: 0xE0 // rotate on specific EOI
|
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
|
because, again, the Closure Compiler does an excellent job inlining such constants (or indeed any property that is
|
||||||
uses the "long form." But again, since the Closure Compiler already does a good job of inlining, it's no longer
|
never modified *or* enumerated).
|
||||||
necessary to use the "long form", which is why you'll see newer code using more conventional Object notation to
|
|
||||||
define class constants.
|
|
||||||
|
|
||||||
### DEBUG vs. RELEASE
|
### DEBUG vs. RELEASE
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
JavaScript Idiosyncrasies
|
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/).
|
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
|
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.
|
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
|
To avoid unexpected type coercion, and thus unexpected matches and/or mismatches, the usual advice is to *always* use
|
||||||
strict equality operators instead ("===" and "!==").
|
strict equality operators ("===" and "!==").
|
||||||
|
|
||||||
I disagree. In properly written code, you should always know what type of data your variables contain. In fact,
|
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
|
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.
|
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*
|
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
|
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) { ... }
|
if (parameter == null) { ... }
|
||||||
|
|
||||||
|
|
@ -30,11 +30,31 @@ whereas strict equality requires more work:
|
||||||
|
|
||||||
if (parameter === undefined || parameter === null) { ... }
|
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
|
Another common pattern:
|
||||||
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.
|
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
|
### Enumerating Array or Object Properties
|
||||||
|
|
||||||
|
|
@ -55,7 +75,7 @@ However, a more elegant solution is to use the unary "+" operator to coerce the
|
||||||
|
|
||||||
+i;
|
+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
|
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:
|
you'd expect. For example:
|
||||||
|
|
@ -71,22 +91,23 @@ So the above example is equivalent to:
|
||||||
n >>>= 0;
|
n >>>= 0;
|
||||||
|
|
||||||
If you really need larger shift counts to work in a consistent manner, you can perform multiple shifts, where each
|
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;
|
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 = 0x88888888|0; // n is displayed as -2004318072
|
||||||
n >>>= 0; // n is displayed as 2290649224
|
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
|
It's true that the bottom 32 bits of the number were not changed, but a side-effect of the unsigned shift operator
|
||||||
that all the upper sign bits are stripped from the (64-bit) result.
|
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
|
I consider this an anomaly of JavaScript's bitwise operators, because it breaks the "rule" that bitwise operators
|
||||||
bits, the upper bits will once be updated to match the sign of the lower 32-bit value:
|
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)*
|
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||||
*March 26, 2015*
|
*March 26, 2015*
|
||||||
|
|
|
||||||
|
|
@ -879,7 +879,11 @@ if (DEBUGGER) {
|
||||||
0x05: [Debugger.INS.LOADALL,Debugger.TYPE_80286],
|
0x05: [Debugger.INS.LOADALL,Debugger.TYPE_80286],
|
||||||
0x06: [Debugger.INS.CLTS, 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],
|
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 = [
|
Debugger.aaGrpDescs = [
|
||||||
|
|
@ -4062,6 +4066,10 @@ if (DEBUGGER) {
|
||||||
return;
|
return;
|
||||||
for (var i = 2; i < asArgs.length; i++) {
|
for (var i = 2; i < asArgs.length; i++) {
|
||||||
var b = str.parseInt(asArgs[i], 16);
|
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.println("setting " + this.hexAddr(aAddr) + " to " + str.toHexByte(b));
|
||||||
this.setByte(aAddr, b, 1);
|
this.setByte(aAddr, b, 1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 = new Array(256);
|
||||||
|
|
||||||
X86.aOps0F[0x00] = X86.opGrp6;
|
X86.aOps0F[0x00] = X86.opGrp6;
|
||||||
|
|
@ -334,6 +386,10 @@ if (I386) {
|
||||||
X86.aOps0F386 = [];
|
X86.aOps0F386 = [];
|
||||||
X86.aOps0F386[0x20] = X86.opMOVrcr;
|
X86.aOps0F386[0x20] = X86.opMOVrcr;
|
||||||
X86.aOps0F386[0x22] = X86.opMOVcrr;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue