Move non-CPU-related update functions to the Computer component

This commit is contained in:
Jeff Parsons 2016-03-30 17:04:36 -07:00
commit e02706ab08
18 changed files with 2557 additions and 2553 deletions

View file

@ -124,7 +124,7 @@
<disk path="/disks/pc/os2/ibm/1.0/OS210-DISK1.json">IBM OS/2 1.0 (1.44M Disk 1)</disk> <disk path="/disks/pc/os2/ibm/1.0/OS210-DISK1.json">IBM OS/2 1.0 (1.44M Disk 1)</disk>
<disk path="/disks/pc/os2/ibm/1.0/OS210-DISK2.json">IBM OS/2 1.0 (1.44M Disk 2)</disk> <disk path="/disks/pc/os2/ibm/1.0/OS210-DISK2.json">IBM OS/2 1.0 (1.44M Disk 2)</disk>
<disk path="/disks/pc/os2/ibm/1.0/OS210-DISK3.json">IBM OS/2 1.0 (1.44M Disk 3)</disk> <disk path="/disks/pc/os2/ibm/1.0/OS210-DISK3.json">IBM OS/2 1.0 (1.44M Disk 3)</disk>
<disk path="/disks/pc/os2/ibm/1.1/OS211-INSTALLATION.json">IBM OS/2 1.1 (1.44M Install)</disk> <disk path="/disks/pc/os2/ibm/1.1/OS211-INSTALL.json">IBM OS/2 1.1 (1.44M Install)</disk>
<disk path="/disks/pc/os2/ibm/1.1/OS211-DISK01.json">IBM OS/2 1.1 (1.44M Disk 1)</disk> <disk path="/disks/pc/os2/ibm/1.1/OS211-DISK01.json">IBM OS/2 1.1 (1.44M Disk 1)</disk>
<disk path="/disks/pc/os2/ibm/1.1/OS211-DISK02.json">IBM OS/2 1.1 (1.44M Disk 2)</disk> <disk path="/disks/pc/os2/ibm/1.1/OS211-DISK02.json">IBM OS/2 1.1 (1.44M Disk 2)</disk>
<disk path="/disks/pc/os2/ibm/1.1/OS211-DISK03.json">IBM OS/2 1.1 (1.44M Disk 3)</disk> <disk path="/disks/pc/os2/ibm/1.1/OS211-DISK03.json">IBM OS/2 1.1 (1.44M Disk 3)</disk>

View file

@ -2,7 +2,7 @@
<?xml-stylesheet type="text/xsl" href="/versions/pcjs/1.21.4/manifest.xsl"?> <?xml-stylesheet type="text/xsl" href="/versions/pcjs/1.21.4/manifest.xsl"?>
<manifest type="software"> <manifest type="software">
<title>IBM OS/2 1.1</title> <title>IBM OS/2 1.1</title>
<disk id="disk00" size="1474560" chs="80:2:18" img="archive/OS211-INSTALLATION.img" href="/disks/pc/os2/ibm/1.1/OS211-INSTALLATION.json" md5="64b1a50f35385f326e7b59b17b417769" md5json="6510aede5a434ae794a3ee9e8d09d6c9"> <disk id="disk00" size="1474560" chs="80:2:18" img="archive/OS211-INSTALL.img" href="/disks/pc/os2/ibm/1.1/OS211-INSTALL.json" md5="64b1a50f35385f326e7b59b17b417769" md5json="6510aede5a434ae794a3ee9e8d09d6c9">
<name>IBM OS/2 1.1 (1.44M Install)</name> <name>IBM OS/2 1.1 (1.44M Install)</name>
</disk> </disk>
<disk id="disk01" size="1474560" chs="80:2:18" img="archive/OS211-DISK01.img" href="/disks/pc/os2/ibm/1.1/OS211-DISK01.json" md5="5b0526049c097e7afa54073f13cf87d1" md5json="f74cb91702c9a502418139601269280d"> <disk id="disk01" size="1474560" chs="80:2:18" img="archive/OS211-DISK01.img" href="/disks/pc/os2/ibm/1.1/OS211-DISK01.json" md5="5b0526049c097e7afa54073f13cf87d1" md5json="f74cb91702c9a502418139601269280d">

View file

@ -1114,4 +1114,7 @@
</xsl:call-template> </xsl:call-template>
</xsl:template> </xsl:template>
<xsl:template name="comment">
</xsl:template>
</xsl:stylesheet> </xsl:stylesheet>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -338,7 +338,7 @@ function DiskDump(sDiskPath, asExclude, sFormat, fComments, sSize, sServerRoot,
this.sDiskPath = path.join(this.sServerRoot, sDiskPath); this.sDiskPath = path.join(this.sServerRoot, sDiskPath);
} }
this.asExclude = asExclude || DiskDump.asExclusions; this.asExclude = asExclude || DiskDump.asExclusions;
this.kbTarget = +sSize || 0; this.kbTarget = sSize|0; // convert the numeric string to a 32-bit number (or 0 if invalid)
this.sFormat = (sFormat || DumpAPI.FORMAT.JSON); this.sFormat = (sFormat || DumpAPI.FORMAT.JSON);
this.fJSONNative = (this.sFormat == DumpAPI.FORMAT.JSON && !fComments); this.fJSONNative = (this.sFormat == DumpAPI.FORMAT.JSON && !fComments);
this.nJSONIndent = 0; this.nJSONIndent = 0;

View file

@ -184,6 +184,14 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
} }
this.dbg = /** @type {Debugger} */ (Component.getComponentByType("Debugger", this.id)); this.dbg = /** @type {Debugger} */ (Component.getComponentByType("Debugger", this.id));
/*
* Enumerate all Video components for future updateVideo() calls.
*/
this.aVideo = [];
for (var video = null; (video = this.getMachineComponent("Video", video));) {
this.aVideo.push(video);
}
/* /*
* Initialize the Bus component * Initialize the Bus component
*/ */
@ -1437,6 +1445,65 @@ Computer.prototype.getMachineComponent = function(sType, componentPrev)
return null; return null;
}; };
/**
* updateFocus(fScroll)
*
* NOTE: When soft keyboard buttons call us to return focus to the machine (and away from the button),
* the scroll feature has annoying effect on iOS, so we no longer do it by default (fScroll must be true).
*
* @this {Computer}
* @param {boolean} [fScroll]
*/
Computer.prototype.updateFocus = function(fScroll)
{
if (this.aVideo.length) {
/*
* This seems to be recommended work-around to prevent the browser from scrolling the focused element
* into view. The CPU is not a visual component, so when the CPU wants to set focus, the primary intent
* is to ensure that keyboard input is fielded properly.
*/
var x = 0, y = 0;
if (fScroll && window) {
x = window.scrollX;
y = window.scrollY;
}
/*
* TODO: We need a mechanism to determine the "active" display, instead of hard-coding this to aVideo[0].
*/
this.aVideo[0].setFocus();
if (fScroll && window) {
window.scrollTo(x, y);
}
}
};
/**
* updateStatus()
*
* @this {Computer}
*/
Computer.prototype.updateStatus = function()
{
if (this.panel) this.panel.updateStatus();
};
/**
* updateVideo(fForce)
*
* Any high-frequency updates should be performed here. Avoid DOM updates, since updateVideo() can be called up to
* 60 times per second (see VIDEO_UPDATES_PER_SECOND).
*
* @this {Computer}
* @param {boolean} [fForce] (true to force a video update)
*/
Computer.prototype.updateVideo = function(fForce)
{
for (var i = 0; i < this.aVideo.length; i++) {
this.aVideo[i].updateScreen(fForce);
}
if (this.panel) this.panel.updateAnimation();
};
/** /**
* Computer.init() * Computer.init()
* *

View file

@ -122,11 +122,6 @@ function CPU(parmsCPU, nCyclesDefault)
this.aCounts.nCyclesChecksumInterval = parmsCPU["csInterval"]; this.aCounts.nCyclesChecksumInterval = parmsCPU["csInterval"];
this.aCounts.nCyclesChecksumStop = parmsCPU["csStop"]; this.aCounts.nCyclesChecksumStop = parmsCPU["csStop"];
/*
* Initially, no video devices are attached that require CPU-driven updates. initBus() will update this.
*/
this.aVideo = [];
this.onRunTimeout = this.runCPU.bind(this); // function onRunTimeout() { cpu.runCPU(); }; this.onRunTimeout = this.runCPU.bind(this); // function onRunTimeout() { cpu.runCPU(); };
this.setReady(); this.setReady();
@ -180,14 +175,6 @@ CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
this.fpu = cmp.getMachineComponent("FPU"); this.fpu = cmp.getMachineComponent("FPU");
/*
* Attach the Video component to the CPU, so that the CPU can periodically update
* the video display via updateVideo(), as cycles permit.
*/
for (var video = null; (video = cmp.getMachineComponent("Video", video));) {
this.aVideo.push(video);
}
/* /*
* Attach the ChipSet component to the CPU so that it can obtain the IDT vector number * Attach the ChipSet component to the CPU so that it can obtain the IDT vector number
* of pending hardware interrupts in response to the ChipSet's updateINTR() notifications. * of pending hardware interrupts in response to the ChipSet's updateINTR() notifications.
@ -323,7 +310,7 @@ CPU.prototype.autoStart = function()
*/ */
if (this.flags.fAutoStart || (!DEBUGGER || !this.dbg) && this.bindings["run"] === undefined) { if (this.flags.fAutoStart || (!DEBUGGER || !this.dbg) && this.bindings["run"] === undefined) {
/* /*
* Now we ALSO set fSetFocus when calling runCPU(), on the assumption that in the "auto-starting" context, * Now we ALSO set fUpdateFocus when calling runCPU(), on the assumption that in the "auto-starting" context,
* a machine without focus is like a day without sunshine. * a machine without focus is like a day without sunshine.
*/ */
this.runCPU(true); this.runCPU(true);
@ -483,66 +470,6 @@ CPU.prototype.displayValue = function(sLabel, nValue, cch)
} }
}; };
/**
* updateStatus(fForce)
*
* This provides periodic Control Panel updates (eg, a few times per second; see STATUS_UPDATES_PER_SECOND).
* The X86CPU subclasses updateStatus() to take care of any DOM updates (eg, register values) while the CPU is running.
*
* @this {CPU}
* @param {boolean} [fForce]
*/
CPU.prototype.updateStatus = function(fForce)
{
if (this.cmp && this.cmp.panel) this.cmp.panel.updateStatus();
};
/**
* updateVideo(fForce)
*
* Any high-frequency updates should be performed here. Avoid DOM updates, since updateVideo() can be called up to
* 60 times per second (see VIDEO_UPDATES_PER_SECOND).
*
* @this {CPU}
* @param {boolean} [fForce] (true to force a video update)
*/
CPU.prototype.updateVideo = function(fForce)
{
for (var i = 0; i < this.aVideo.length; i++) {
this.aVideo[i].updateScreen(fForce);
}
if (this.cmp && this.cmp.panel) this.cmp.panel.updateAnimation();
};
/**
* setFocus(fScroll)
*
* NOTE: When soft keyboard buttons call us to return focus to the machine (and away from the button),
* the scroll feature has annoying effect on iOS, so we no longer do it by default (fScroll must be true).
*
* @this {CPU}
* @param {boolean} [fScroll]
*/
CPU.prototype.setFocus = function(fScroll)
{
if (this.aVideo.length) {
/*
* This seems to be recommended work-around to prevent the browser from scrolling the focused element
* into view. The CPU is not a visual component, so when the CPU wants to set focus, the primary intent
* is to ensure that keyboard input is fielded properly.
*/
var x = 0, y = 0;
if (fScroll && window) {
x = window.scrollX;
y = window.scrollY;
}
this.aVideo[0].setFocus();
if (fScroll && window) {
window.scrollTo(x, y);
}
}
};
/** /**
* setBinding(sHTMLType, sBinding, control, sValue) * setBinding(sHTMLType, sBinding, control, sValue)
* *
@ -823,20 +750,20 @@ CPU.prototype.getSpeedTarget = function()
}; };
/** /**
* setSpeed(nMultiplier, fSetFocus) * setSpeed(nMultiplier, fUpdateFocus)
* *
* NOTE: This used to return the target speed, in mhz, but no callers appear to care at this point. * NOTE: This used to return the target speed, in mhz, but no callers appear to care at this point.
* *
* @this {CPU} * @this {CPU}
* @param {number} [nMultiplier] is the new proposed multiplier (reverts to 1 if the target was too high) * @param {number} [nMultiplier] is the new proposed multiplier (reverts to 1 if the target was too high)
* @param {boolean} [fSetFocus] is true to give the CPU focus * @param {boolean} [fUpdateFocus] is true to give the Computer focus
* @return {boolean} true if successful, false if not * @return {boolean} true if successful, false if not
* *
* @desc Whenever the speed is changed, the running cycle count and corresponding start time must be reset, * @desc Whenever the speed is changed, the running cycle count and corresponding start time must be reset,
* so that the next effective speed calculation obtains sensible results. In fact, when runCPU() initially calls * so that the next effective speed calculation obtains sensible results. In fact, when runCPU() initially calls
* setSpeed() with no parameters, that's all this function does (it doesn't change the current speed setting). * setSpeed() with no parameters, that's all this function does (it doesn't change the current speed setting).
*/ */
CPU.prototype.setSpeed = function(nMultiplier, fSetFocus) CPU.prototype.setSpeed = function(nMultiplier, fUpdateFocus)
{ {
var fSuccess = false; var fSuccess = false;
if (nMultiplier !== undefined) { if (nMultiplier !== undefined) {
@ -857,7 +784,7 @@ CPU.prototype.setSpeed = function(nMultiplier, fSetFocus)
if (controlSpeed) controlSpeed.textContent = sSpeed; if (controlSpeed) controlSpeed.textContent = sSpeed;
this.println("target speed: " + sSpeed); this.println("target speed: " + sSpeed);
} }
if (fSetFocus) this.setFocus(); if (fUpdateFocus && this.cmp) this.cmp.updateFocus();
} }
this.addCycles(this.nRunCycles); this.addCycles(this.nRunCycles);
this.nRunCycles = 0; this.nRunCycles = 0;
@ -1014,12 +941,12 @@ CPU.prototype.calcRemainingTime = function()
}; };
/** /**
* runCPU(fSetFocus) * runCPU(fUpdateFocus)
* *
* @this {CPU} * @this {CPU}
* @param {boolean} [fSetFocus] is true to give the CPU focus * @param {boolean} [fUpdateFocus] is true to update Computer focus
*/ */
CPU.prototype.runCPU = function(fSetFocus) CPU.prototype.runCPU = function(fUpdateFocus)
{ {
if (!this.setBusy(true)) { if (!this.setBusy(true)) {
this.updateCPU(); this.updateCPU();
@ -1027,7 +954,7 @@ CPU.prototype.runCPU = function(fSetFocus)
return; return;
} }
this.startCPU(fSetFocus); this.startCPU(fUpdateFocus);
/* /*
* calcStartTime() initializes the cycle counter and timestamp for this runCPU() invocation, and optionally * calcStartTime() initializes the cycle counter and timestamp for this runCPU() invocation, and optionally
@ -1078,13 +1005,13 @@ CPU.prototype.runCPU = function(fSetFocus)
this.aCounts.nCyclesNextVideoUpdate -= nCycles; this.aCounts.nCyclesNextVideoUpdate -= nCycles;
if (this.aCounts.nCyclesNextVideoUpdate <= 0) { if (this.aCounts.nCyclesNextVideoUpdate <= 0) {
this.aCounts.nCyclesNextVideoUpdate += this.aCounts.nCyclesPerVideoUpdate; this.aCounts.nCyclesNextVideoUpdate += this.aCounts.nCyclesPerVideoUpdate;
this.updateVideo(); if (this.cmp) this.cmp.updateVideo();
} }
this.aCounts.nCyclesNextStatusUpdate -= nCycles; this.aCounts.nCyclesNextStatusUpdate -= nCycles;
if (this.aCounts.nCyclesNextStatusUpdate <= 0) { if (this.aCounts.nCyclesNextStatusUpdate <= 0) {
this.aCounts.nCyclesNextStatusUpdate += this.aCounts.nCyclesPerStatusUpdate; this.aCounts.nCyclesNextStatusUpdate += this.aCounts.nCyclesPerStatusUpdate;
this.updateStatus(); if (this.cmp) this.cmp.updateStatus();
} }
this.aCounts.nCyclesNextYield -= nCycles; this.aCounts.nCyclesNextYield -= nCycles;
@ -1106,13 +1033,13 @@ CPU.prototype.runCPU = function(fSetFocus)
}; };
/** /**
* startCPU(fSetFocus) * startCPU(fUpdateFocus)
* *
* WARNING: Other components must use runCPU() to get the CPU running; this is a runCPU() helper function only. * WARNING: Other components must use runCPU() to get the CPU running; this is a runCPU() helper function only.
* *
* @param {boolean} [fSetFocus] * @param {boolean} [fUpdateFocus]
*/ */
CPU.prototype.startCPU = function(fSetFocus) CPU.prototype.startCPU = function(fUpdateFocus)
{ {
if (!this.flags.fRunning) { if (!this.flags.fRunning) {
/* /*
@ -1128,8 +1055,10 @@ CPU.prototype.startCPU = function(fSetFocus)
if (this.chipset) this.chipset.setSpeaker(); if (this.chipset) this.chipset.setSpeaker();
var controlRun = this.bindings["run"]; var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Halt"; if (controlRun) controlRun.textContent = "Halt";
this.updateStatus(true); if (this.cmp) {
if (fSetFocus) this.setFocus(true); this.cmp.updateStatus();
if (fUpdateFocus) this.cmp.updateFocus(true);
}
} }
}; };
@ -1187,8 +1116,10 @@ CPU.prototype.stopCPU = function(fComplete)
*/ */
CPU.prototype.updateCPU = function(fForce) CPU.prototype.updateCPU = function(fForce)
{ {
this.updateVideo(fForce); if (this.cmp) {
this.updateStatus(); this.cmp.updateVideo(fForce);
this.cmp.updateStatus();
}
}; };
/** /**

View file

@ -2182,11 +2182,11 @@ if (DEBUGGER) {
}; };
/** /**
* setFocus() * updateFocus()
* *
* @this {Debugger} * @this {Debugger}
*/ */
Debugger.prototype.setFocus = function() Debugger.prototype.updateFocus = function()
{ {
if (this.controlDebug) this.controlDebug.focus(); if (this.controlDebug) this.controlDebug.focus();
}; };
@ -3945,16 +3945,16 @@ if (DEBUGGER) {
}; };
/** /**
* runCPU(fSetFocus) * runCPU(fUpdateFocus)
* *
* @this {Debugger} * @this {Debugger}
* @param {boolean} [fSetFocus] is true to give the CPU focus * @param {boolean} [fUpdateFocus] is true to update focus
* @return {boolean} true if run request successful, false if not * @return {boolean} true if run request successful, false if not
*/ */
Debugger.prototype.runCPU = function(fSetFocus) Debugger.prototype.runCPU = function(fUpdateFocus)
{ {
if (!this.isCPUAvail()) return false; if (!this.isCPUAvail()) return false;
this.cpu.runCPU(fSetFocus); this.cpu.runCPU(fUpdateFocus);
return true; return true;
}; };
@ -4258,7 +4258,7 @@ if (DEBUGGER) {
this.println(sStopped); this.println(sStopped);
} }
this.updateStatus(true); this.updateStatus(true);
this.setFocus(); this.updateFocus();
this.clearTempBreakpoint(this.cpu.regLIP); this.clearTempBreakpoint(this.cpu.regLIP);
} }
}; };
@ -7618,7 +7618,7 @@ if (DEBUGGER) {
if (this.nStep) { if (this.nStep) {
this.setTempBreakpoint(dbgAddr); this.setTempBreakpoint(dbgAddr);
if (!this.runCPU()) { if (!this.runCPU()) {
this.cpu.setFocus(); if (this.cmp) this.cmp.updateFocus();
this.nStep = 0; this.nStep = 0;
} }
/* /*

View file

@ -1438,7 +1438,11 @@ FDC.prototype.doneLoadDiskette = function onFDCLoadNotify(drive, disk, sDiskette
drive.nDiskHeads = aDiskInfo[1]; drive.nDiskHeads = aDiskInfo[1];
drive.nDiskSectors = aDiskInfo[2]; drive.nDiskSectors = aDiskInfo[2];
if (this.cpu) this.cpu.setFocus(); /*
* Since you usually want the Computer to have focus again after loading a new diskette, let's try automatically
* updating the focus after a successful load.
*/
if (this.cmp) this.cmp.updateFocus();
} }
else { else {
drive.fLocal = false; drive.fLocal = false;

View file

@ -1187,7 +1187,7 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
case "caps-lock": case "caps-lock":
this.bindings[id] = control; this.bindings[id] = control;
control.onclick = function onClickCapsLock(event) { control.onclick = function onClickCapsLock(event) {
if (kbd.cpu) kbd.cpu.setFocus(); if (kbd.cmp) kbd.cmp.updateFocus();
return kbd.toggleCapsLock(); return kbd.toggleCapsLock();
}; };
return true; return true;
@ -1195,7 +1195,7 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
case "num-lock": case "num-lock":
this.bindings[id] = control; this.bindings[id] = control;
control.onclick = function onClickNumLock(event) { control.onclick = function onClickNumLock(event) {
if (kbd.cpu) kbd.cpu.setFocus(); if (kbd.cmp) kbd.cmp.updateFocus();
return kbd.toggleNumLock(); return kbd.toggleNumLock();
}; };
return true; return true;
@ -1203,7 +1203,7 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
case "scroll-lock": case "scroll-lock":
this.bindings[id] = control; this.bindings[id] = control;
control.onclick = function onClickScrollLock(event) { control.onclick = function onClickScrollLock(event) {
if (kbd.cpu) kbd.cpu.setFocus(); if (kbd.cmp) kbd.cmp.updateFocus();
return kbd.toggleScrollLock(); return kbd.toggleScrollLock();
}; };
return true; return true;
@ -1218,7 +1218,7 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
control.onclick = function(kbd, sKey, simCode) { control.onclick = function(kbd, sKey, simCode) {
return function onClickKeyboard(event) { return function onClickKeyboard(event) {
if (!COMPILED && kbd.messageEnabled()) kbd.printMessage(sKey + " clicked", Messages.KEYS); if (!COMPILED && kbd.messageEnabled()) kbd.printMessage(sKey + " clicked", Messages.KEYS);
if (kbd.cpu) kbd.cpu.setFocus(); if (kbd.cmp) kbd.cmp.updateFocus();
kbd.updateShiftState(simCode, true); // future-proofing if/when any LOCK keys are added to CLICKCODES kbd.updateShiftState(simCode, true); // future-proofing if/when any LOCK keys are added to CLICKCODES
kbd.addActiveKey(simCode, true); kbd.addActiveKey(simCode, true);
}; };
@ -1255,7 +1255,7 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
*/ */
this.bindings[id] = control; this.bindings[id] = control;
control.onclick = function onClickTest(event) { control.onclick = function onClickTest(event) {
if (kbd.cpu) kbd.cpu.setFocus(); if (kbd.cmp) kbd.cmp.updateFocus();
return kbd.injectKeys(sValue); return kbd.injectKeys(sValue);
}; };
return true; return true;
@ -1323,6 +1323,7 @@ Keyboard.prototype.findBinding = function(simCode, sType, fDown)
*/ */
Keyboard.prototype.initBus = function(cmp, bus, cpu, dbg) Keyboard.prototype.initBus = function(cmp, bus, cpu, dbg)
{ {
this.cmp = cmp;
this.bus = bus; this.bus = bus;
this.cpu = cpu; this.cpu = cpu;
this.dbg = dbg; this.dbg = dbg;

View file

@ -5758,28 +5758,20 @@ Video.prototype.updateScreenGraphicsEGA = function(addrBuffer, addrScreen, addrS
if (x < xDirty) xDirty = x; if (x < xDirty) xDirty = x;
for (iPixel = 0; iPixel < nPixels; iPixel++) { for (iPixel = 0; iPixel < nPixels; iPixel++) {
/* /*
* We must follow the golden JavaScript rule of appending "|0" to all hex constants with bit 31 set. * 0x80808080 may LOOK like a 32-bit value, but it is not, because JavaScript treats it as a POSITIVE
* The innocuous use of the bit-wise OR operator has the side-effect of producing a negative value, * number, and therefore outside the normal 32-bit integer range; however, the AND operator guarantees
* matching how entries in Video.aEGADWToByte are initialized (eg, "Video.aEGADWToByte[0x80000000|0]"). * that the result will be a 32-bit value, so it doesn't matter.
*/
var dwPixel = data & (0x80808080|0);
/*
* This was the old approach to dealing with negative hex values, by converting them to positive
* values that didn't alter the low 32 bits. But it's not ideal, because it requires using values here
* and in the array that are outside the signed 32-bit range, potentially triggering floating-point.
*
* if (dwPixel < 0) dwPixel += 0x100000000;
*
* An even simpler solution would be to use the unsigned right-shift operator:
*
* dwPixel >>> 0
*
* but again, all that does is produce a value outside the signed 32-bit range, which is sub-optimal.
*/ */
var dwPixel = data & 0x80808080;
this.assert(Video.aEGADWToByte[dwPixel] !== undefined); this.assert(Video.aEGADWToByte[dwPixel] !== undefined);
/* /*
* Since assertions don't fix problems (only catch them, and only in DEBUG builds), I'm also ensuring * Since assertions don't fix problems (only catch them, and only in DEBUG builds), I'm also ensuring
* that bPixel will always default to 0 if an undefined value ever slips through again. * that bPixel will default to 0 if an undefined value ever slips through again.
*
* How did an undefined value slip through? We had (incorrectly) initialized entries in aEGADWToByte;
* for example, we used to set aEGADWToByte[0x80808080] instead of aEGADWToByte[0x80808080|0]. The
* former is a POSITIVE index that is outside the 32-bit integer range, whereas the latter is a NEGATIVE
* index, which is what this code requires.
*/ */
var bPixel = Video.aEGADWToByte[dwPixel] || 0; var bPixel = Video.aEGADWToByte[dwPixel] || 0;
this.setPixel(this.imageScreenBuffer, x++, y, aPixelColors[bPixel]); this.setPixel(this.imageScreenBuffer, x++, y, aPixelColors[bPixel]);
@ -5923,8 +5915,8 @@ Video.prototype.getRetraceBits = function(card)
/* /*
* NOTE: The CGA bits CGA.STATUS.RETRACE (0x01) and CGA.STATUS.VRETRACE (0x08) match the EGA definitions, * NOTE: The CGA bits CGA.STATUS.RETRACE (0x01) and CGA.STATUS.VRETRACE (0x08) match the EGA definitions,
* and they also correspond to the MDA bits MDA.STATUS.HDRIVE (0x01) and MDA.STATUS.BWVIDEO (0x08); I'm not sure why * and they also correspond to the MDA bits MDA.STATUS.HDRIVE (0x01) and MDA.STATUS.BWVIDEO (0x08); I'm not sure
* the MDA uses different designations, but the bits appear to serve the same purpose. * why the MDA uses different designations, but the bits appear to serve the same purpose.
* *
* TODO: Decide whether this more faithful emulation of the retrace bits should be extended to the MDA/CGA, too; * TODO: Decide whether this more faithful emulation of the retrace bits should be extended to the MDA/CGA, too;
* doing so might slow down the BIOS scroll code a bit, though. * doing so might slow down the BIOS scroll code a bit, though.

View file

@ -1116,4 +1116,7 @@
</xsl:call-template> </xsl:call-template>
</xsl:template> </xsl:template>
<xsl:template name="comment">
</xsl:template>
</xsl:stylesheet> </xsl:stylesheet>

View file

@ -624,21 +624,21 @@ web.downloadFile = function(sData, sType, fBase64, sFileName)
sURI += (fBase64? sData : encodeURI(sData)); sURI += (fBase64? sData : encodeURI(sData));
} else { } else {
sURI += (fBase64? sData : encodeURIComponent(sData)); sURI += (fBase64? sData : encodeURIComponent(sData));
if (sFileName) { }
link = document.createElement('a'); if (sFileName) {
if (typeof link.download != 'string') link = null; link = document.createElement('a');
} if (typeof link.download != 'string') link = null;
} }
if (link) { if (link) {
link.href = sURI; link.href = sURI;
link.download = sFileName; link.download = sFileName;
document.body.appendChild(link); // Firefox requires the link to be in the body document.body.appendChild(link); // Firefox allegedly requires the link to be in the body
link.click(); link.click();
document.body.removeChild(link); document.body.removeChild(link);
sAlert = 'Check your Downloads folder for "' + sFileName + '"'; sAlert = 'Check your Downloads folder for ' + sFileName + '.';
} else { } else {
window.open(sURI); window.open(sURI);
sAlert = 'Check your browser for a new window/tab containing the requested data'; sAlert = 'Check your browser for a new window/tab containing the requested data (' + sFileName + ').';
} }
return sAlert; return sAlert;
}; };

View file

@ -1114,4 +1114,7 @@
</xsl:call-template> </xsl:call-template>
</xsl:template> </xsl:template>
<xsl:template name="comment">
</xsl:template>
</xsl:stylesheet> </xsl:stylesheet>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff