Fixed space handling on iOS 9.1
This commit is contained in:
parent
3bad5547c2
commit
bf75b8a059
5 changed files with 136 additions and 50 deletions
|
|
@ -47,11 +47,11 @@ if (NODE) {
|
||||||
*
|
*
|
||||||
* The Keyboard component can be configured with the following (parmsKbd) properties:
|
* The Keyboard component can be configured with the following (parmsKbd) properties:
|
||||||
*
|
*
|
||||||
* model: model string; should be one of:
|
* model: keyboard model string, which must match one of the values listed in Keyboard.MODELS:
|
||||||
*
|
*
|
||||||
* us83 (default)
|
* "US83" (default)
|
||||||
* us84 (TODO: awaiting implementation)
|
* "US84"
|
||||||
* us101 (TODO: awaiting implementation)
|
* "US101"
|
||||||
*
|
*
|
||||||
* Its main purpose is to receive binding requests for various keyboard events, and to use those events
|
* Its main purpose is to receive binding requests for various keyboard events, and to use those events
|
||||||
* to simulate the PC's keyboard hardware.
|
* to simulate the PC's keyboard hardware.
|
||||||
|
|
@ -64,7 +64,7 @@ function Keyboard(parmsKbd)
|
||||||
{
|
{
|
||||||
Component.call(this, "Keyboard", parmsKbd, Keyboard, Messages.KEYBOARD);
|
Component.call(this, "Keyboard", parmsKbd, Keyboard, Messages.KEYBOARD);
|
||||||
|
|
||||||
this.nDefaultModel = parmsKbd['model'];
|
this.setModel(parmsKbd['model']);
|
||||||
|
|
||||||
this.fMobile = web.isMobile();
|
this.fMobile = web.isMobile();
|
||||||
this.fMSIE = web.isUserAgent("MSIE");
|
this.fMSIE = web.isUserAgent("MSIE");
|
||||||
|
|
@ -148,6 +148,11 @@ function Keyboard(parmsKbd)
|
||||||
|
|
||||||
Component.subclass(Keyboard);
|
Component.subclass(Keyboard);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Supported keyboard models (the first entry is the default if the specified model isn't recognized)
|
||||||
|
*/
|
||||||
|
Keyboard.MODELS = ["US83", "US84", "US101"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alphanumeric and other common (printable) ASCII codes.
|
* Alphanumeric and other common (printable) ASCII codes.
|
||||||
*
|
*
|
||||||
|
|
@ -197,6 +202,7 @@ Keyboard.KEYCODE = {
|
||||||
/* 0x13 */ PAUSE: 19, // PAUSE/BREAK
|
/* 0x13 */ PAUSE: 19, // PAUSE/BREAK
|
||||||
/* 0x14 */ CAPS_LOCK: 20,
|
/* 0x14 */ CAPS_LOCK: 20,
|
||||||
/* 0x1B */ ESC: 27,
|
/* 0x1B */ ESC: 27,
|
||||||
|
/* 0x20 */ SPACE: 32,
|
||||||
/* 0x21 */ PGUP: 33,
|
/* 0x21 */ PGUP: 33,
|
||||||
/* 0x22 */ PGDN: 34,
|
/* 0x22 */ PGDN: 34,
|
||||||
/* 0x23 */ END: 35,
|
/* 0x23 */ END: 35,
|
||||||
|
|
@ -339,6 +345,7 @@ Keyboard.SIMCODE = {
|
||||||
ALT: Keyboard.KEYCODE.ALT + Keyboard.KEYCODE.ONDOWN,
|
ALT: Keyboard.KEYCODE.ALT + Keyboard.KEYCODE.ONDOWN,
|
||||||
CAPS_LOCK: Keyboard.KEYCODE.CAPS_LOCK + Keyboard.KEYCODE.ONDOWN,
|
CAPS_LOCK: Keyboard.KEYCODE.CAPS_LOCK + Keyboard.KEYCODE.ONDOWN,
|
||||||
ESC: Keyboard.KEYCODE.ESC + Keyboard.KEYCODE.ONDOWN,
|
ESC: Keyboard.KEYCODE.ESC + Keyboard.KEYCODE.ONDOWN,
|
||||||
|
SPACE: Keyboard.KEYCODE.SPACE + Keyboard.KEYCODE.ONDOWN,
|
||||||
F1: Keyboard.KEYCODE.F1 + Keyboard.KEYCODE.ONDOWN,
|
F1: Keyboard.KEYCODE.F1 + Keyboard.KEYCODE.ONDOWN,
|
||||||
F2: Keyboard.KEYCODE.F2 + Keyboard.KEYCODE.ONDOWN,
|
F2: Keyboard.KEYCODE.F2 + Keyboard.KEYCODE.ONDOWN,
|
||||||
F3: Keyboard.KEYCODE.F3 + Keyboard.KEYCODE.ONDOWN,
|
F3: Keyboard.KEYCODE.F3 + Keyboard.KEYCODE.ONDOWN,
|
||||||
|
|
@ -673,7 +680,7 @@ Keyboard.SOFTCODES = {
|
||||||
/* 54 */ 'right-shift': Keyboard.SIMCODE.RSHIFT, // formerly "rshift"
|
/* 54 */ 'right-shift': Keyboard.SIMCODE.RSHIFT, // formerly "rshift"
|
||||||
/* 55 */ 'prtsc': Keyboard.SIMCODE.PRTSC, // unshifted '*'; becomes dedicated 'Print Screen' key on 101-key keyboards
|
/* 55 */ 'prtsc': Keyboard.SIMCODE.PRTSC, // unshifted '*'; becomes dedicated 'Print Screen' key on 101-key keyboards
|
||||||
/* 56 */ 'alt': Keyboard.SIMCODE.ALT,
|
/* 56 */ 'alt': Keyboard.SIMCODE.ALT,
|
||||||
/* 57 */ 'space': Keyboard.ASCII[' '],
|
/* 57 */ 'space': Keyboard.SIMCODE.SPACE,
|
||||||
/* 58 */ 'caps-lock': Keyboard.SIMCODE.CAPS_LOCK,
|
/* 58 */ 'caps-lock': Keyboard.SIMCODE.CAPS_LOCK,
|
||||||
/* 59 */ 'f1': Keyboard.SIMCODE.F1,
|
/* 59 */ 'f1': Keyboard.SIMCODE.F1,
|
||||||
/* 60 */ 'f2': Keyboard.SIMCODE.F2,
|
/* 60 */ 'f2': Keyboard.SIMCODE.F2,
|
||||||
|
|
@ -858,7 +865,7 @@ Keyboard.SIMCODES[Keyboard.ASCII['?']] = Keyboard.SCANCODE.SLASH | (K
|
||||||
Keyboard.SIMCODES[Keyboard.SIMCODE.RSHIFT] = Keyboard.SCANCODE.RSHIFT;
|
Keyboard.SIMCODES[Keyboard.SIMCODE.RSHIFT] = Keyboard.SCANCODE.RSHIFT;
|
||||||
Keyboard.SIMCODES[Keyboard.SIMCODE.PRTSC] = Keyboard.SCANCODE.PRTSC;
|
Keyboard.SIMCODES[Keyboard.SIMCODE.PRTSC] = Keyboard.SCANCODE.PRTSC;
|
||||||
Keyboard.SIMCODES[Keyboard.SIMCODE.ALT] = Keyboard.SCANCODE.ALT;
|
Keyboard.SIMCODES[Keyboard.SIMCODE.ALT] = Keyboard.SCANCODE.ALT;
|
||||||
Keyboard.SIMCODES[Keyboard.ASCII[' ']] = Keyboard.SCANCODE.SPACE;
|
Keyboard.SIMCODES[Keyboard.SIMCODE.SPACE] = Keyboard.SCANCODE.SPACE;
|
||||||
Keyboard.SIMCODES[Keyboard.SIMCODE.CAPS_LOCK] = Keyboard.SCANCODE.CAPS_LOCK;
|
Keyboard.SIMCODES[Keyboard.SIMCODE.CAPS_LOCK] = Keyboard.SCANCODE.CAPS_LOCK;
|
||||||
Keyboard.SIMCODES[Keyboard.SIMCODE.F1] = Keyboard.SCANCODE.F1;
|
Keyboard.SIMCODES[Keyboard.SIMCODE.F1] = Keyboard.SCANCODE.F1;
|
||||||
Keyboard.SIMCODES[Keyboard.SIMCODE.F2] = Keyboard.SCANCODE.F2;
|
Keyboard.SIMCODES[Keyboard.SIMCODE.F2] = Keyboard.SCANCODE.F2;
|
||||||
|
|
@ -1150,13 +1157,26 @@ Keyboard.prototype.notifyEscape = function(fDisabled, fAllDown)
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setModel(nModel)
|
* setModel(sModel)
|
||||||
|
*
|
||||||
|
* This breaks a model string (eg, "US83") into two parts: modelCountry (eg, "US") and modelKeys (eg, 83).
|
||||||
|
* If the model string isn't recognized, we use Keyboard.MODELS[0] (ie, the first entry in the model array).
|
||||||
*
|
*
|
||||||
* @this {Keyboard}
|
* @this {Keyboard}
|
||||||
* @param {number} nModel
|
* @param {string|undefined} sModel
|
||||||
*/
|
*/
|
||||||
Keyboard.prototype.setModel = function(nModel)
|
Keyboard.prototype.setModel = function(sModel)
|
||||||
{
|
{
|
||||||
|
var iModel = 0;
|
||||||
|
this.model = null;
|
||||||
|
if (sModel) {
|
||||||
|
this.model = sModel.toUpperCase();
|
||||||
|
iModel = Keyboard.MODELS.indexOf(this.model);
|
||||||
|
if (iModel < 0) iModel = 0;
|
||||||
|
}
|
||||||
|
sModel = Keyboard.MODELS[iModel];
|
||||||
|
this.modelCountry = sModel.substr(0, 2);
|
||||||
|
this.modelKeys = parseInt(sModel.substr(2), 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1383,7 +1403,22 @@ Keyboard.prototype.powerDown = function(fSave, fShutdown)
|
||||||
*/
|
*/
|
||||||
Keyboard.prototype.reset = function()
|
Keyboard.prototype.reset = function()
|
||||||
{
|
{
|
||||||
this.setModel(this.nDefaultModel);
|
/*
|
||||||
|
* If no keyboard model was specified, our initial setModel() call will select the "US83" keyboard as the
|
||||||
|
* default, but now that the ChipSet is initialized, we can pick a better default, based on the ChipSet model.
|
||||||
|
*/
|
||||||
|
if (!this.model && this.chipset) {
|
||||||
|
switch(this.chipset.model) {
|
||||||
|
case ChipSet.MODEL_5150:
|
||||||
|
case ChipSet.MODEL_5160:
|
||||||
|
this.setModel(Keyboard.MODELS[0]);
|
||||||
|
break;
|
||||||
|
case ChipSet.MODEL_5170:
|
||||||
|
default:
|
||||||
|
this.setModel(Keyboard.MODELS[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.initState();
|
this.initState();
|
||||||
|
|
||||||
|
|
@ -1881,7 +1916,7 @@ Keyboard.prototype.updateActiveKey = function(key, msTimer)
|
||||||
this.printMessage((msTimer? '\n' : "") + "updateActiveKey(" + key.simCode + (msTimer? "," + msTimer + "ms" : "") + "): " + (key.fDown? "down" : "up"), true);
|
this.printMessage((msTimer? '\n' : "") + "updateActiveKey(" + key.simCode + (msTimer? "," + msTimer + "ms" : "") + "): " + (key.fDown? "down" : "up"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.keySimulate(key.simCode, key.fDown);
|
if (!this.keySimulate(key.simCode, key.fDown)) return;
|
||||||
|
|
||||||
if (!key.nRepeat) return;
|
if (!key.nRepeat) return;
|
||||||
|
|
||||||
|
|
@ -2173,6 +2208,14 @@ Keyboard.prototype.keySimulate = function(simCode, fDown)
|
||||||
|
|
||||||
var abScanCodes = [];
|
var abScanCodes = [];
|
||||||
var bCode = wCode & 0xff;
|
var bCode = wCode & 0xff;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: Update the following restrictions to address 84-key and 101-key keyboards limitations.
|
||||||
|
*/
|
||||||
|
if (bCode > 83 && this.modelKeys == 83) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
abScanCodes.push(bCode | (fDown? 0 : Keyboard.SCANCODE.BREAK));
|
abScanCodes.push(bCode | (fDown? 0 : Keyboard.SCANCODE.BREAK));
|
||||||
|
|
||||||
var fAlpha = (simCode >= Keyboard.ASCII.A && simCode <= Keyboard.ASCII.Z || simCode >= Keyboard.ASCII.a && simCode <= Keyboard.ASCII.z);
|
var fAlpha = (simCode >= Keyboard.ASCII.A && simCode <= Keyboard.ASCII.Z || simCode >= Keyboard.ASCII.a && simCode <= Keyboard.ASCII.z);
|
||||||
|
|
|
||||||
|
|
@ -343,12 +343,23 @@ SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control)
|
||||||
|
|
||||||
switch (sBinding) {
|
switch (sBinding) {
|
||||||
case SerialPort.sIOBuffer:
|
case SerialPort.sIOBuffer:
|
||||||
|
/*
|
||||||
|
* TODO: Figure out how to make this control activate the soft keyboard on iOS; since
|
||||||
|
* this control has the "readonly" attribute by default, iOS refuses to activate the
|
||||||
|
* keyboard, and adding the "contenteditable" attribute doesn't override that behavior.
|
||||||
|
*
|
||||||
|
* Removing the "readonly" attribute works, but then every key press results in double
|
||||||
|
* characters; presumably that could be overcome by processing all keys in the onKeyDown()
|
||||||
|
* handler instead, but that would require keyCode mapping tables, because down events
|
||||||
|
* are not the same as press events.
|
||||||
|
*/
|
||||||
this.bindings[sBinding] = this.controlIOBuffer = control;
|
this.bindings[sBinding] = this.controlIOBuffer = control;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* By establishing an onkeypress handler here, we make it possible for DOS commands like
|
* By establishing an onkeypress handler here, we make it possible for DOS commands like
|
||||||
* "CTTY COM1" to more or less work (use "CTTY CON" to restore control to the DOS console).
|
* "CTTY COM1" to more or less work (use "CTTY CON" to restore control to the DOS console).
|
||||||
*/
|
*/
|
||||||
control.onkeydown = function onKeyDownSerial(event) {
|
control.onkeydown = function onKeyDown(event) {
|
||||||
/*
|
/*
|
||||||
* This is required in addition to onkeypress, because it's the only way to prevent
|
* This is required in addition to onkeypress, because it's the only way to prevent
|
||||||
* BACKSPACE (keyCode 8) from being interpreted by the browser as a "Back" operation;
|
* BACKSPACE (keyCode 8) from being interpreted by the browser as a "Back" operation;
|
||||||
|
|
@ -369,18 +380,28 @@ SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control)
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
control.onkeypress = function onKeyPressSerial(event) {
|
control.onkeypress = function onKeyPress(event) {
|
||||||
/*
|
/*
|
||||||
* Browser-independent keyCode extraction (refer to keyPress() and the other key
|
* Browser-independent keyCode extraction; refer to keyPress() and the other key event
|
||||||
* event handlers in keyboard.js).
|
* handlers in keyboard.js.
|
||||||
*
|
|
||||||
* The additional check for SPACE (keyCode 0x20) and subsequent preventDefault() call
|
|
||||||
* prevents SPACE from bubbling up to the document event handlers, where its default
|
|
||||||
* behavior is typically to scroll the entire page -- a real nuisance.
|
|
||||||
*/
|
*/
|
||||||
event = event || window.event;
|
event = event || window.event;
|
||||||
var keyCode = event.which || event.keyCode;
|
var keyCode = event.which || event.keyCode;
|
||||||
serial.sendRBR([keyCode]);
|
serial.sendRBR([keyCode]);
|
||||||
|
/*
|
||||||
|
* This additional check for SPACE (keyCode 0x20) and subsequent preventDefault()
|
||||||
|
* prevents SPACE from bubbling up to the document event handlers, where the default
|
||||||
|
* behavior is typically to scroll the entire page -- a real nuisance.
|
||||||
|
*
|
||||||
|
* Keyboard.onKeyPress() has a similar issue, but it seems to be limited to Safari on iOS,
|
||||||
|
* first noticed on iOS 9.1. The problem there is that Safari's default SPACE behavior
|
||||||
|
* occurs BEFORE the onkeypress handler is called, so we would have to call preventDefault()
|
||||||
|
* in the onkeydown handler, but then the onkeypress handler would no longer be called.
|
||||||
|
*
|
||||||
|
* So, to resolve the Keyboard.onKeyPress() issue, we now define SPACE as an ONDOWN key,
|
||||||
|
* so that onKeyDown() will process the SPACE key immediately and automatically invoke
|
||||||
|
* preventDefault().
|
||||||
|
*/
|
||||||
if (keyCode == 0x20) {
|
if (keyCode == 0x20) {
|
||||||
if (event.preventDefault) event.preventDefault();
|
if (event.preventDefault) event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ var X86 = {
|
||||||
* Priority: Instruction exception, TRAP, NMI, Processor Extension Segment Overrun, and finally INTR.
|
* Priority: Instruction exception, TRAP, NMI, Processor Extension Segment Overrun, and finally INTR.
|
||||||
*
|
*
|
||||||
* All exceptions can also occur in real-mode, except where noted. A GP_FAULT in real-mode can be triggered
|
* All exceptions can also occur in real-mode, except where noted. A GP_FAULT in real-mode can be triggered
|
||||||
* by "any memory reference instruction that attempts to reference [a] 16-bit word at offset 0FFFFH".
|
* by "any memory reference instruction that attempts to reference [a] 16-bit word at offset 0xFFFF".
|
||||||
*
|
*
|
||||||
* Interrupts beyond 0x10 (up through 0x1F) are reserved for future exceptions.
|
* Interrupts beyond 0x10 (up through 0x1F) are reserved for future exceptions.
|
||||||
*
|
*
|
||||||
|
|
@ -137,11 +137,11 @@ var X86 = {
|
||||||
DF: 0x0400, // bit 10: Direction flag
|
DF: 0x0400, // bit 10: Direction flag
|
||||||
OF: 0x0800, // bit 11: Overflow flag
|
OF: 0x0800, // bit 11: Overflow flag
|
||||||
IOPL: {
|
IOPL: {
|
||||||
MASK: 0x3000, // bits 12-13: I/O Privilege Level (always set on 8086/80186, clear on 80286 reset)
|
MASK: 0x3000, // bits 12-13: I/O Privilege Level (always set on 8086/80186; clear on 80286 reset)
|
||||||
SHIFT: 12
|
SHIFT: 12
|
||||||
},
|
},
|
||||||
NT: 0x4000, // bit 14: Nested Task flag (always set on 8086/80186, clear on 80286 reset)
|
NT: 0x4000, // bit 14: Nested Task flag (always set on 8086/80186; clear on 80286 reset)
|
||||||
BIT15: 0x8000, // bit 15: reserved (always set on 8086/80186, clear otherwise)
|
BIT15: 0x8000, // bit 15: reserved (always set on 8086/80186; clear otherwise)
|
||||||
RF: 0x10000, // bit 16: Resume Flag (temporarily disables debug exceptions; 80386 only)
|
RF: 0x10000, // bit 16: Resume Flag (temporarily disables debug exceptions; 80386 only)
|
||||||
VM: 0x20000 // bit 17: Virtual 8086 Mode (80386 only)
|
VM: 0x20000 // bit 17: Virtual 8086 Mode (80386 only)
|
||||||
},
|
},
|
||||||
|
|
@ -461,7 +461,7 @@ var X86 = {
|
||||||
SS: 0x36, // opSS()
|
SS: 0x36, // opSS()
|
||||||
DS: 0x3E, // opDS()
|
DS: 0x3E, // opDS()
|
||||||
PUSHSP: 0x54, // opPUSHSP()
|
PUSHSP: 0x54, // opPUSHSP()
|
||||||
PUSHA: 0x60, // opPUHSA() (80186 and up)
|
PUSHA: 0x60, // opPUSHA() (80186 and up)
|
||||||
POPA: 0x61, // opPOPA() (80186 and up)
|
POPA: 0x61, // opPOPA() (80186 and up)
|
||||||
BOUND: 0x62, // opBOUND() (80186 and up)
|
BOUND: 0x62, // opBOUND() (80186 and up)
|
||||||
ARPL: 0x63, // opARPL() (80286 and up)
|
ARPL: 0x63, // opARPL() (80286 and up)
|
||||||
|
|
@ -519,12 +519,12 @@ var X86 = {
|
||||||
UD2: 0x0B0F // UD2 (invalid opcode "guaranteed" to generate UD_FAULT on all post-8086 processors)
|
UD2: 0x0B0F // UD2 (invalid opcode "guaranteed" to generate UD_FAULT on all post-8086 processors)
|
||||||
},
|
},
|
||||||
/*
|
/*
|
||||||
* Floating Point Unit (FPU), aka Numeric Data Processor (NDP), aka Numeric Processor Extension (NPX) definitions
|
* Floating Point Unit (FPU), aka Numeric Data Processor (NDP), aka Numeric Processor Extension (NPX), aka Coprocessor definitions
|
||||||
*/
|
*/
|
||||||
FPU: {
|
FPU: {
|
||||||
MODEL_8087: 8087,
|
MODEL_8087: 8087,
|
||||||
MODEL_80287: 80287,
|
MODEL_80287: 80287,
|
||||||
MODEL_80287XL: 80387, // internally, the 80287XL was an 80387SX, so in general, we treat this as MODEL_80387
|
MODEL_80287XL: 80387, // internally, the 80287XL was an 80387SX, so generally, we treat this as MODEL_80387
|
||||||
MODEL_80387: 80387,
|
MODEL_80387: 80387,
|
||||||
CONTROL: { // FPU Control Word
|
CONTROL: { // FPU Control Word
|
||||||
IM: 0x0001, // bit 0: Invalid Operation Mask
|
IM: 0x0001, // bit 0: Invalid Operation Mask
|
||||||
|
|
@ -534,18 +534,18 @@ var X86 = {
|
||||||
UM: 0x0010, // bit 4: Underflow Mask
|
UM: 0x0010, // bit 4: Underflow Mask
|
||||||
PM: 0x0020, // bit 5: Precision Mask
|
PM: 0x0020, // bit 5: Precision Mask
|
||||||
EXC: 0x003F, // all of the above exceptions
|
EXC: 0x003F, // all of the above exceptions
|
||||||
// bit 6: unused
|
|
||||||
IEM: 0x0080, // bit 7: Interrupt Enable Mask (0 enables interrupts, 1 masks them; 8087 only)
|
IEM: 0x0080, // bit 7: Interrupt Enable Mask (0 enables interrupts, 1 masks them; 8087 only)
|
||||||
PC: 0x0300, // bits 8-9: Precision Control
|
PC: 0x0300, // bits 8-9: Precision Control
|
||||||
RC: 0x0C00, // bits 10-11: Rounding Control
|
RC: { // bits 10-11: Rounding Control
|
||||||
RC_NEAR: 0x0000,
|
NEAR: 0x0000,
|
||||||
RC_DOWN: 0x0400,
|
DOWN: 0x0400,
|
||||||
RC_UP: 0x0800,
|
UP: 0x0800,
|
||||||
RC_CHOP: 0x0C00,
|
CHOP: 0x0C00,
|
||||||
|
MASK: 0x0C00
|
||||||
|
},
|
||||||
IC: 0x1000, // bit 12: Infinity Control (0 for Projective, 1 for Affine)
|
IC: 0x1000, // bit 12: Infinity Control (0 for Projective, 1 for Affine)
|
||||||
// bits 13-15: unused
|
UNUSED: 0xE040, // bits 6,13-15: unused
|
||||||
INIT: 0x03BF, // X86.FPU.CONTROL.IM | X86.FPU.CONTROL.DM | X86.FPU.CONTROL.ZM | X86.FPU.CONTROL.OM | X86.FPU.CONTROL.UM | X86.FPU.CONTROL.PM | X86.FPU.CONTROL.IEM | X86.FPU.CONTROL.PC
|
INIT: 0x03BF // X86.FPU.CONTROL.IM | X86.FPU.CONTROL.DM | X86.FPU.CONTROL.ZM | X86.FPU.CONTROL.OM | X86.FPU.CONTROL.UM | X86.FPU.CONTROL.PM | X86.FPU.CONTROL.IEM | X86.FPU.CONTROL.PC
|
||||||
UNUSED: 0xE040
|
|
||||||
},
|
},
|
||||||
STATUS: { // FPU Status Word
|
STATUS: { // FPU Status Word
|
||||||
IE: 0x0001, // bit 0: Invalid Operation
|
IE: 0x0001, // bit 0: Invalid Operation
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ if (NODE) {
|
||||||
*
|
*
|
||||||
* FPU Coprocessor Trivia
|
* FPU Coprocessor Trivia
|
||||||
*
|
*
|
||||||
* Microsoft C libraries executed software interrupts in the range 0x34-0x3B immediately after
|
* Microsoft C 4.00 libraries executed software interrupts in the range 0x34-0x3B immediately after
|
||||||
* FPU operations, to assist with floating-point emulation when no coprocessor was present, since
|
* FPU operations, to assist with floating-point emulation when no coprocessor was present, since
|
||||||
* processors prior to the 80286 had no mechanism for generating a fault when an unsupported FPU
|
* processors prior to the 80286 had no mechanism for generating a fault when an unsupported FPU
|
||||||
* instruction was executed.
|
* instruction was executed.
|
||||||
|
|
@ -68,7 +68,7 @@ if (NODE) {
|
||||||
* In short, INT 0x34 through INT 0x3B was used after ESC opcodes 0xD8 through 0xDF, INT 0x3C was
|
* In short, INT 0x34 through INT 0x3B was used after ESC opcodes 0xD8 through 0xDF, INT 0x3C was
|
||||||
* used for FPU instructions containing a segment override, and INT 0x3D was used for FWAIT.
|
* used for FPU instructions containing a segment override, and INT 0x3D was used for FWAIT.
|
||||||
*
|
*
|
||||||
* A sample piece of code is available in x86ops.js, because it also highlights the Microsoft C
|
* A sample piece of code is available in x86ops.js, because it also highlights the Microsoft C 4.00
|
||||||
* library's dependency on the 8086/8088 behavior of "PUSH SP" (see the opPUSHSP_8086() function).
|
* library's dependency on the 8086/8088 behavior of "PUSH SP" (see the opPUSHSP_8086() function).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -276,6 +276,8 @@ X86FPU.prototype.restore = function(data)
|
||||||
* us whenever an I/O operation that resets the coprocessor is performed. Only 80487 coprocessors and higher will
|
* us whenever an I/O operation that resets the coprocessor is performed. Only 80487 coprocessors and higher will
|
||||||
* also clear the "exception" registers, but the 80487 is currently beyond my planned level of support.
|
* also clear the "exception" registers, but the 80487 is currently beyond my planned level of support.
|
||||||
*
|
*
|
||||||
|
* TODO: Add support for X86.FPU.CONTROL.PC (Precision Control) and X86.FPU.CONTROL.IC (Infinity Control)
|
||||||
|
*
|
||||||
* @this {X86FPU}
|
* @this {X86FPU}
|
||||||
*/
|
*/
|
||||||
X86FPU.prototype.resetFPU = function()
|
X86FPU.prototype.resetFPU = function()
|
||||||
|
|
@ -287,8 +289,8 @@ X86FPU.prototype.resetFPU = function()
|
||||||
this.iST = 0; // the ST bits for regStatus are actually stored here
|
this.iST = 0; // the ST bits for regStatus are actually stored here
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
/*
|
/*
|
||||||
* All the registers were tagged "unused" above, which is all that would normally happen,
|
* All the registers were tagged "unused" above, which is all that would normally happen, but debugging is
|
||||||
* but debugging is a little easier if we also zero everything, too.
|
* a little easier if we zero all the registers as well.
|
||||||
*/
|
*/
|
||||||
for (var iReg = 0; iReg < this.regStack.length; iReg++) {
|
for (var iReg = 0; iReg < this.regStack.length; iReg++) {
|
||||||
this.regStack[iReg] = 0.0;
|
this.regStack[iReg] = 0.0;
|
||||||
|
|
@ -315,7 +317,7 @@ X86FPU.prototype.isModel = function(model)
|
||||||
/**
|
/**
|
||||||
* isAtLeastModel(model)
|
* isAtLeastModel(model)
|
||||||
*
|
*
|
||||||
* If the current model is greater than or equal to the specified model, then it's assumed the
|
* If the current model is greater than or equal to the specified model, then it's assumed that the
|
||||||
* current operation is supported, and we return true.
|
* current operation is supported, and we return true.
|
||||||
*
|
*
|
||||||
* @this {X86FPU}
|
* @this {X86FPU}
|
||||||
|
|
@ -334,9 +336,9 @@ X86FPU.prototype.isAtLeastModel = function(model)
|
||||||
* to a "temp-real" (REAL80) and back again losslessly, otherwise a bug in either getTRFromLR() or getLRFromTR()
|
* to a "temp-real" (REAL80) and back again losslessly, otherwise a bug in either getTRFromLR() or getLRFromTR()
|
||||||
* might exist. That test code can be resurrected from the repo; this code is being retained for future tests.
|
* might exist. That test code can be resurrected from the repo; this code is being retained for future tests.
|
||||||
*
|
*
|
||||||
* NOTE: If either min or max is a value containing 32 or more bits AND bit 31 is set AND it has passed
|
* NOTE: If either min or max is a value containing 32 or more significant bits AND bit 31 is set AND it has passed
|
||||||
* through some bit-wise operation(s), then that value may end up being negative, so you may end up with an
|
* through some bit-wise operation(s), then that value may end up being negative, so you may end up with an inverted
|
||||||
* inverted (or empty) range or other unexpected results.
|
* range, or a range that's smaller or larger than intended.
|
||||||
*
|
*
|
||||||
* @this {X86FPU}
|
* @this {X86FPU}
|
||||||
* @param {number} min (inclusive)
|
* @param {number} min (inclusive)
|
||||||
|
|
@ -712,16 +714,16 @@ X86FPU.prototype.roundInteger = function(operand, max)
|
||||||
{
|
{
|
||||||
if (operand == null) return null;
|
if (operand == null) return null;
|
||||||
|
|
||||||
var rc = (this.regControl & X86.FPU.CONTROL.RC), result;
|
var rc = (this.regControl & X86.FPU.CONTROL.RC.MASK), result;
|
||||||
|
|
||||||
if (rc == X86.FPU.CONTROL.RC_NEAR) {
|
if (rc == X86.FPU.CONTROL.RC.NEAR) {
|
||||||
result = Math.round(operand);
|
result = Math.round(operand);
|
||||||
if (result - operand === 0.5 && (result % 2)) result--;
|
if (result - operand === 0.5 && (result % 2)) result--;
|
||||||
}
|
}
|
||||||
else if (rc == X86.FPU.CONTROL.RC_DOWN || rc == X86.FPU.CONTROL.RC_CHOP && operand > 0) {
|
else if (rc == X86.FPU.CONTROL.RC.DOWN || rc == X86.FPU.CONTROL.RC.CHOP && operand > 0) {
|
||||||
result = Math.floor(operand);
|
result = Math.floor(operand);
|
||||||
}
|
}
|
||||||
else { // X86.FPU.CONTROL.RC_UP or X86.FPU.CONTROL.RC_CHOP && operand <= 0
|
else { // X86.FPU.CONTROL.RC.UP or X86.FPU.CONTROL.RC.CHOP && operand <= 0
|
||||||
result = Math.ceil(operand);
|
result = Math.ceil(operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1417,6 +1419,8 @@ X86FPU.prototype.saveEnv = function(addr)
|
||||||
/**
|
/**
|
||||||
* opFPU(bOpcode, bModRM, dst, src)
|
* opFPU(bOpcode, bModRM, dst, src)
|
||||||
*
|
*
|
||||||
|
* This is called by the CPU's ESC opcode handlers, after each instruction has been fully decoded.
|
||||||
|
*
|
||||||
* @this {X86FPU}
|
* @this {X86FPU}
|
||||||
* @param {number} bOpcode (0xD8-0xDF)
|
* @param {number} bOpcode (0xD8-0xDF)
|
||||||
* @param {number} bModRM
|
* @param {number} bModRM
|
||||||
|
|
@ -1485,6 +1489,23 @@ X86FPU.prototype.opFPU = function(bOpcode, bModRM, dst, src)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* opWAIT()
|
||||||
|
*
|
||||||
|
* This is called by the CPU's WAIT opcode handler, giving us the opportunity to synchronize the FPU with the CPU,
|
||||||
|
* charge an appropriate number of cycles, and return true. In this context, it's considered an FWAIT instruction,
|
||||||
|
* but technically, it's the same opcode.
|
||||||
|
*
|
||||||
|
* If we choose to do nothing, then we must return false, so that the CPU can charge a default number of cycles.
|
||||||
|
*
|
||||||
|
* @this {X86FPU}
|
||||||
|
* @return {boolean} true if implemented, false if not
|
||||||
|
*/
|
||||||
|
X86FPU.prototype.opWAIT = function()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
if (DEBUGGER) {
|
if (DEBUGGER) {
|
||||||
/**
|
/**
|
||||||
* readFPUStack(i)
|
* readFPUStack(i)
|
||||||
|
|
|
||||||
|
|
@ -2600,8 +2600,9 @@ X86.opCALLF = function()
|
||||||
*/
|
*/
|
||||||
X86.opWAIT = function()
|
X86.opWAIT = function()
|
||||||
{
|
{
|
||||||
this.printMessage("WAIT not implemented");
|
if (!this.fpu || !this.fpu.opWAIT()) {
|
||||||
this.nStepCycles--;
|
this.nStepCycles -= 3; // X86FPU.opWAIT() is required to charge some number of cycles if it returns true
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue