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

View file

@ -2,7 +2,7 @@
<?xml-stylesheet type="text/xsl" href="/versions/pcjs/1.21.4/manifest.xsl"?>
<manifest type="software">
<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>
</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">

View file

@ -1114,4 +1114,7 @@
</xsl:call-template>
</xsl:template>
<xsl:template name="comment">
</xsl:template>
</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.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.fJSONNative = (this.sFormat == DumpAPI.FORMAT.JSON && !fComments);
this.nJSONIndent = 0;

View file

@ -184,6 +184,14 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
}
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
*/
@ -1437,6 +1445,65 @@ Computer.prototype.getMachineComponent = function(sType, componentPrev)
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()
*

View file

@ -122,11 +122,6 @@ function CPU(parmsCPU, nCyclesDefault)
this.aCounts.nCyclesChecksumInterval = parmsCPU["csInterval"];
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.setReady();
@ -180,14 +175,6 @@ CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
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
* 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) {
/*
* 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.
*/
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)
*
@ -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.
*
* @this {CPU}
* @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
*
* @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
* 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;
if (nMultiplier !== undefined) {
@ -857,7 +784,7 @@ CPU.prototype.setSpeed = function(nMultiplier, fSetFocus)
if (controlSpeed) controlSpeed.textContent = sSpeed;
this.println("target speed: " + sSpeed);
}
if (fSetFocus) this.setFocus();
if (fUpdateFocus && this.cmp) this.cmp.updateFocus();
}
this.addCycles(this.nRunCycles);
this.nRunCycles = 0;
@ -1014,12 +941,12 @@ CPU.prototype.calcRemainingTime = function()
};
/**
* runCPU(fSetFocus)
* runCPU(fUpdateFocus)
*
* @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)) {
this.updateCPU();
@ -1027,7 +954,7 @@ CPU.prototype.runCPU = function(fSetFocus)
return;
}
this.startCPU(fSetFocus);
this.startCPU(fUpdateFocus);
/*
* 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;
if (this.aCounts.nCyclesNextVideoUpdate <= 0) {
this.aCounts.nCyclesNextVideoUpdate += this.aCounts.nCyclesPerVideoUpdate;
this.updateVideo();
if (this.cmp) this.cmp.updateVideo();
}
this.aCounts.nCyclesNextStatusUpdate -= nCycles;
if (this.aCounts.nCyclesNextStatusUpdate <= 0) {
this.aCounts.nCyclesNextStatusUpdate += this.aCounts.nCyclesPerStatusUpdate;
this.updateStatus();
if (this.cmp) this.cmp.updateStatus();
}
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.
*
* @param {boolean} [fSetFocus]
* @param {boolean} [fUpdateFocus]
*/
CPU.prototype.startCPU = function(fSetFocus)
CPU.prototype.startCPU = function(fUpdateFocus)
{
if (!this.flags.fRunning) {
/*
@ -1128,8 +1055,10 @@ CPU.prototype.startCPU = function(fSetFocus)
if (this.chipset) this.chipset.setSpeaker();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Halt";
this.updateStatus(true);
if (fSetFocus) this.setFocus(true);
if (this.cmp) {
this.cmp.updateStatus();
if (fUpdateFocus) this.cmp.updateFocus(true);
}
}
};
@ -1187,8 +1116,10 @@ CPU.prototype.stopCPU = function(fComplete)
*/
CPU.prototype.updateCPU = function(fForce)
{
this.updateVideo(fForce);
this.updateStatus();
if (this.cmp) {
this.cmp.updateVideo(fForce);
this.cmp.updateStatus();
}
};
/**

View file

@ -2182,11 +2182,11 @@ if (DEBUGGER) {
};
/**
* setFocus()
* updateFocus()
*
* @this {Debugger}
*/
Debugger.prototype.setFocus = function()
Debugger.prototype.updateFocus = function()
{
if (this.controlDebug) this.controlDebug.focus();
};
@ -3945,16 +3945,16 @@ if (DEBUGGER) {
};
/**
* runCPU(fSetFocus)
* runCPU(fUpdateFocus)
*
* @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
*/
Debugger.prototype.runCPU = function(fSetFocus)
Debugger.prototype.runCPU = function(fUpdateFocus)
{
if (!this.isCPUAvail()) return false;
this.cpu.runCPU(fSetFocus);
this.cpu.runCPU(fUpdateFocus);
return true;
};
@ -4258,7 +4258,7 @@ if (DEBUGGER) {
this.println(sStopped);
}
this.updateStatus(true);
this.setFocus();
this.updateFocus();
this.clearTempBreakpoint(this.cpu.regLIP);
}
};
@ -7618,7 +7618,7 @@ if (DEBUGGER) {
if (this.nStep) {
this.setTempBreakpoint(dbgAddr);
if (!this.runCPU()) {
this.cpu.setFocus();
if (this.cmp) this.cmp.updateFocus();
this.nStep = 0;
}
/*

View file

@ -1438,7 +1438,11 @@ FDC.prototype.doneLoadDiskette = function onFDCLoadNotify(drive, disk, sDiskette
drive.nDiskHeads = aDiskInfo[1];
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 {
drive.fLocal = false;

View file

@ -1187,7 +1187,7 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
case "caps-lock":
this.bindings[id] = control;
control.onclick = function onClickCapsLock(event) {
if (kbd.cpu) kbd.cpu.setFocus();
if (kbd.cmp) kbd.cmp.updateFocus();
return kbd.toggleCapsLock();
};
return true;
@ -1195,7 +1195,7 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
case "num-lock":
this.bindings[id] = control;
control.onclick = function onClickNumLock(event) {
if (kbd.cpu) kbd.cpu.setFocus();
if (kbd.cmp) kbd.cmp.updateFocus();
return kbd.toggleNumLock();
};
return true;
@ -1203,7 +1203,7 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
case "scroll-lock":
this.bindings[id] = control;
control.onclick = function onClickScrollLock(event) {
if (kbd.cpu) kbd.cpu.setFocus();
if (kbd.cmp) kbd.cmp.updateFocus();
return kbd.toggleScrollLock();
};
return true;
@ -1218,7 +1218,7 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
control.onclick = function(kbd, sKey, simCode) {
return function onClickKeyboard(event) {
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.addActiveKey(simCode, true);
};
@ -1255,7 +1255,7 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
*/
this.bindings[id] = control;
control.onclick = function onClickTest(event) {
if (kbd.cpu) kbd.cpu.setFocus();
if (kbd.cmp) kbd.cmp.updateFocus();
return kbd.injectKeys(sValue);
};
return true;
@ -1323,6 +1323,7 @@ Keyboard.prototype.findBinding = function(simCode, sType, fDown)
*/
Keyboard.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.cmp = cmp;
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;

View file

@ -5758,28 +5758,20 @@ Video.prototype.updateScreenGraphicsEGA = function(addrBuffer, addrScreen, addrS
if (x < xDirty) xDirty = x;
for (iPixel = 0; iPixel < nPixels; iPixel++) {
/*
* We must follow the golden JavaScript rule of appending "|0" to all hex constants with bit 31 set.
* The innocuous use of the bit-wise OR operator has the side-effect of producing a negative value,
* matching how entries in Video.aEGADWToByte are initialized (eg, "Video.aEGADWToByte[0x80000000|0]").
*/
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.
* 0x80808080 may LOOK like a 32-bit value, but it is not, because JavaScript treats it as a POSITIVE
* number, and therefore outside the normal 32-bit integer range; however, the AND operator guarantees
* that the result will be a 32-bit value, so it doesn't matter.
*/
var dwPixel = data & 0x80808080;
this.assert(Video.aEGADWToByte[dwPixel] !== undefined);
/*
* 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;
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,
* and they also correspond to the MDA bits MDA.STATUS.HDRIVE (0x01) and MDA.STATUS.BWVIDEO (0x08); I'm not sure why
* the MDA uses different designations, but the bits appear to serve the same purpose.
* and they also correspond to the MDA bits MDA.STATUS.HDRIVE (0x01) and MDA.STATUS.BWVIDEO (0x08); I'm not sure
* 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;
* doing so might slow down the BIOS scroll code a bit, though.

View file

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

View file

@ -624,21 +624,21 @@ web.downloadFile = function(sData, sType, fBase64, sFileName)
sURI += (fBase64? sData : encodeURI(sData));
} else {
sURI += (fBase64? sData : encodeURIComponent(sData));
if (sFileName) {
link = document.createElement('a');
if (typeof link.download != 'string') link = null;
}
}
if (sFileName) {
link = document.createElement('a');
if (typeof link.download != 'string') link = null;
}
if (link) {
link.href = sURI;
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();
document.body.removeChild(link);
sAlert = 'Check your Downloads folder for "' + sFileName + '"';
sAlert = 'Check your Downloads folder for ' + sFileName + '.';
} else {
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;
};

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff