Fixed space handling on iOS 9.1

This commit is contained in:
Jeff Parsons 2015-12-01 19:22:03 -08:00
commit bf75b8a059
5 changed files with 136 additions and 50 deletions

View file

@ -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);

View file

@ -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();
} }

View file

@ -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

View file

@ -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)

View file

@ -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
}
}; };
/** /**