Merge branch 'master' into gh-pages
This commit is contained in:
commit
3398a9fdd1
18 changed files with 3019 additions and 2919 deletions
|
|
@ -8,7 +8,7 @@ redirect_from:
|
|||
---
|
||||
|
||||
IBM PC Machine Configurations
|
||||
---
|
||||
-----------------------------
|
||||
|
||||
PCjs supports these early [IBM PC Machines](#ibm-pc-machines):
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,19 @@
|
|||
<fdc id="fdcNEC" pos="left" padLeft="8px" padBottom="8px">
|
||||
<control type="list" binding="listDrives"/>
|
||||
<control type="list" binding="listDisks">
|
||||
<disk path="/disks/pcx86/dos/ibm/0.90/PCDOS090.json">PC-DOS 0.90</disk>
|
||||
<disk path="/disks/pcx86/dos/ibm/1.00/PCDOS100.json">PC-DOS 1.00</disk>
|
||||
<disk path="/disks/pcx86/dos/ibm/1.10/PCDOS110.json">PC-DOS 1.10</disk>
|
||||
<disk path="/disks/pcx86/dos/ibm/2.00/PCDOS200-DISK1.json">PC-DOS 2.00 (Disk 1)</disk>
|
||||
<disk path="/disks/pcx86/dos/ibm/2.00/PCDOS200-DISK2.json">PC-DOS 2.00 (Disk 2)</disk>
|
||||
<disk path="/disks/pcx86/dos/ibm/2.10/PCDOS210-DISK1.json">PC-DOS 2.10 (Disk 1)</disk>
|
||||
<disk path="/disks/pcx86/dos/ibm/2.10/PCDOS210-DISK2.json">PC-DOS 2.10 (Disk 2)</disk>
|
||||
<disk path="/disks/pcx86/dos/ibm/3.00/PCDOS300-DISK1.json">PC-DOS 3.00 (Disk 1)</disk>
|
||||
<disk path="/disks/pcx86/dos/ibm/3.00/PCDOS300-DISK2.json">PC-DOS 3.00 (Disk 2)</disk>
|
||||
<disk path="/disks/pcx86/dos/ibm/3.10/PCDOS310-DISK1.json">PC-DOS 3.10 (Disk 1)</disk>
|
||||
<disk path="/disks/pcx86/dos/ibm/3.10/PCDOS310-DISK2.json">PC-DOS 3.10 (Disk 2)</disk>
|
||||
<disk path="/disks/pcx86/dos/ibm/3.20/PCDOS320-DISK1.json">PC-DOS 3.20 (Disk 1)</disk>
|
||||
<disk path="/disks/pcx86/dos/ibm/3.20/PCDOS320-DISK2.json">PC-DOS 3.20 (Disk 2)</disk>
|
||||
<disk path="/disks/pcx86/dos/microsoft/3.20/MSDOS320-DISK1.json">MS-DOS 3.20 (Disk 1)</disk>
|
||||
<disk path="/disks/pcx86/dos/microsoft/3.20/MSDOS320-DISK2.json">MS-DOS 3.20 (Disk 2)</disk>
|
||||
<disk path="/disks/pcx86/dos/microsoft/3.20/MSDOS320-DISK3-PROGREF1.json">MS-DOS 3.20 (ProgRef 1)</disk>
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ PC-SIG Library 8th Edition CD-ROM (April 1990)
|
|||
|
||||
The machine below has access to all 2,121 diskette images from the PC-SIG Library 8th Edition CD-ROM (April 1990).
|
||||
The directory listings were generated from the folder's [manifest](manifest.xml) using
|
||||
[disklist](/modules/diskdump/bin/disklist), a custom PCjs JavaScript utility. The same machine is also available
|
||||
[with the PCjs Debugger](debugger/).
|
||||
<a href="/modules/diskdump/bin/disklist" download="disklist.js">disklist</a>, a custom PCjs JavaScript utility.
|
||||
The same machine is also available [with the PCjs Debugger](debugger/).
|
||||
|
||||
{% include machine.html id="pcsig08" %}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,14 @@
|
|||
<fdc id="fdcNEC" pos="left" padLeft="8px" padBottom="8px">
|
||||
<control type="list" binding="listDrives"/>
|
||||
<control type="list" binding="listDisks">
|
||||
<manifest ref="/disks/pcx86/dos/ibm/0.90/manifest.xml"/>
|
||||
<manifest ref="/disks/pcx86/dos/ibm/1.00/manifest.xml"/>
|
||||
<manifest ref="/disks/pcx86/dos/ibm/1.10/manifest.xml"/>
|
||||
<manifest ref="/disks/pcx86/dos/ibm/2.00/manifest.xml"/>
|
||||
<manifest ref="/disks/pcx86/dos/ibm/2.10/manifest.xml"/>
|
||||
<manifest ref="/disks/pcx86/dos/ibm/3.00/manifest.xml"/>
|
||||
<manifest ref="/disks/pcx86/dos/ibm/3.10/manifest.xml"/>
|
||||
<manifest ref="/disks/pcx86/dos/ibm/3.20/manifest.xml"/>
|
||||
<manifest ref="/disks/pcx86/dos/microsoft/3.20/manifest.xml"/>
|
||||
<manifest ref="/disks/pcx86/shareware/pcsig08/manifest.xml"/>
|
||||
</control>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
layout: page
|
||||
title: Microsoft Windows 95 (First Retail Release)
|
||||
permalink: /disks/pcx86/windows/win95/4.00.950/
|
||||
redirect_from:
|
||||
- /disks/pc/windows/win95/4.00.950/
|
||||
machines:
|
||||
- id: deskpro386
|
||||
type: pcx86
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -5,10 +5,11 @@ permalink: /modules/pcx86/
|
|||
---
|
||||
|
||||
IBM PC Emulation Module (PCx86)
|
||||
===
|
||||
===============================
|
||||
|
||||
Overview
|
||||
---
|
||||
--------
|
||||
|
||||
PCx86 is the IBM PC emulation module powering all our [IBM PC Machines](/devices/pcx86/machine/).
|
||||
|
||||
This module divides PC functionality into variety of logical and visual components.
|
||||
|
|
@ -84,7 +85,7 @@ referencing other components. Device components should define an initBus() noti
|
|||
*Computer* component will call after it has created/initialized the *Bus* component.
|
||||
|
||||
Features
|
||||
---
|
||||
--------
|
||||
|
||||
[List of major existing features goes here]
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ permalink: /modules/pcx86/bin/
|
|||
---
|
||||
|
||||
Running PCx86 From The Command-Line
|
||||
---
|
||||
-----------------------------------
|
||||
|
||||
In this *bin* directory, run:
|
||||
|
||||
|
|
@ -26,15 +26,19 @@ The following output should appear:
|
|||
PCx86 v1.x.x
|
||||
Copyright © 2012-2017 Jeff Parsons <Jeff@pcjs.org>
|
||||
License: GPL version 3 or later <http://gnu.org/licenses/gpl.html>
|
||||
warning: Machine component type 'HDC' not found
|
||||
warning: Machine component type 'Mouse' not found
|
||||
ibm5150.pc-mda-64k object created
|
||||
PCx86> bus: 8Kb ROM at 000FE000
|
||||
bus: 32Kb ROM at 000F6000
|
||||
bus: 576Kb RAM at 00000000
|
||||
bus: 4Kb VIDEO at 000B0000
|
||||
PCx86> Bus: 8Kb ROM at 000FE000
|
||||
Bus: 32Kb ROM at 000F6000
|
||||
notice: Mounted diskette "PC-DOS 2.00 (Disk 1)" in drive A
|
||||
notice: Mounted diskette "PC-DOS 2.00 (Disk 2)" in drive B
|
||||
Bus: 576Kb RAM at 0000
|
||||
Bus: 4Kb VIDEO at 000B0000
|
||||
Type ? for help with PCx86 Debugger commands
|
||||
AX=0000 BX=0000 CX=0000 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
|
||||
SS=0000 DS=0000 ES=0000 PS=F002 V0 D0 I0 T0 S0 Z0 A0 P0 C0
|
||||
&FFFF:0000 EA5BE000F0 JMP &F000:E05B (romBIOS+0x0000005B)
|
||||
&FFFF:0000 EA5BE000F0 JMP &F000:E05B (romBIOS+0x005B)
|
||||
|
||||
Start the machine with a `g` command:
|
||||
|
||||
|
|
|
|||
|
|
@ -893,6 +893,28 @@ class Computer extends Component {
|
|||
*/
|
||||
checkPower()
|
||||
{
|
||||
if (this.flags.unloading) {
|
||||
/*
|
||||
* We happen to know that we're currently only called by the CPU's onClickRun() function, so
|
||||
* if the unloading flag is set, then we've somehow gotten into a weird state where the machine
|
||||
* thinks it's being (or has been) unloaded by the browser, but in fact, it has not.
|
||||
*
|
||||
* The only time I've seen this happen is when the user clicks a link on a page that the browser
|
||||
* decided to treat as a download operation, instead of loading a new page. The proper way to
|
||||
* resolve that confusion is to set the "download" attribute on the link (which will prevent the
|
||||
* page's "onbeforeunload" handler from being called in the first place), but we cannot guarantee
|
||||
* that all such links will have their "download" attribute properly set.
|
||||
*
|
||||
* Hence, this code: we do the same thing that the show() function does, which is to attempt a
|
||||
* REPOWER operation. If that doesn't result in the powered flag getting turned back on, well,
|
||||
* then we're probably screwed.
|
||||
*/
|
||||
this.flags.unloading = false;
|
||||
if (this.flags.initDone && !this.flags.powered) {
|
||||
this.powerOn(Computer.RESUME_REPOWER);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.flags.powered) return true;
|
||||
|
||||
var component = null, iComponent;
|
||||
|
|
@ -1703,6 +1725,9 @@ class Computer extends Component {
|
|||
var computer = /** @type {Computer} */ (Component.getComponentByType("Computer", parmsComputer['id']));
|
||||
if (computer) {
|
||||
|
||||
/*
|
||||
* Clear new flag that Component functions (eg, notice()) should check before alerting the user.
|
||||
*/
|
||||
computer.flags.unloading = false;
|
||||
|
||||
if (DEBUG && computer.messageEnabled()) {
|
||||
|
|
@ -1710,7 +1735,7 @@ class Computer extends Component {
|
|||
}
|
||||
|
||||
if (computer.flags.initDone && !computer.flags.powered) {
|
||||
/**
|
||||
/*
|
||||
* Repower the computer, notifying every component to continue running as-is.
|
||||
*/
|
||||
computer.powerOn(Computer.RESUME_REPOWER);
|
||||
|
|
@ -1755,7 +1780,7 @@ class Computer extends Component {
|
|||
if (computer) {
|
||||
|
||||
/*
|
||||
* Added a new flag that Component functions (eg, notice()) should check before alerting the user.
|
||||
* Set new flag that Component functions (eg, notice()) should check before alerting the user.
|
||||
*/
|
||||
computer.flags.unloading = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -472,11 +472,20 @@ class CPU extends Component {
|
|||
case "run":
|
||||
this.bindings[sBinding] = control;
|
||||
control.onclick = function onClickRun() {
|
||||
var fRunning = cpu.flags.running;
|
||||
if (!cpu.cmp || !cpu.cmp.checkPower()) return;
|
||||
if (!cpu.flags.running)
|
||||
cpu.runCPU(true);
|
||||
else
|
||||
cpu.stopCPU(true);
|
||||
/*
|
||||
* We snapped the CPU's running flag before calling checkPower() because there are rare (REPOWER)
|
||||
* situations where checkPower() will have started the CPU as well. So toggle the CPU state ONLY
|
||||
* if the running flag remains unchanged.
|
||||
*/
|
||||
if (fRunning == cpu.flags.running) {
|
||||
if (!cpu.flags.running) {
|
||||
cpu.runCPU(true);
|
||||
} else {
|
||||
cpu.stopCPU(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
fBound = true;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -2353,10 +2353,10 @@ class Video extends Component {
|
|||
* is exasperating; browsers can't agree on 'full' or 'Full, 'request' or 'Request', 'screen' or 'Screen', and
|
||||
* while some browsers honor other browser prefixes, most browsers don't.
|
||||
*/
|
||||
this.doFullScreen = null;
|
||||
if ((this.container = container)) {
|
||||
this.doFullScreen = container['requestFullscreen'] || container['msRequestFullscreen'] || container['mozRequestFullScreen'] || container['webkitRequestFullscreen'];
|
||||
if (this.doFullScreen) {
|
||||
this.container = container;
|
||||
if (this.container) {
|
||||
this.container.doFullScreen = container['requestFullscreen'] || container['msRequestFullscreen'] || container['mozRequestFullScreen'] || container['webkitRequestFullscreen'];
|
||||
if (this.container.doFullScreen) {
|
||||
for (i = 0; i < asWebPrefixes.length; i++) {
|
||||
sEvent = asWebPrefixes[i] + 'fullscreenchange';
|
||||
if ('on' + sEvent in document) {
|
||||
|
|
@ -2572,7 +2572,7 @@ class Video extends Component {
|
|||
switch (sBinding) {
|
||||
|
||||
case "fullScreen":
|
||||
if (this.doFullScreen) {
|
||||
if (this.container && this.container.doFullScreen) {
|
||||
control.onclick = function onClickFullScreen() {
|
||||
if (DEBUG) video.printMessage("fullScreen()");
|
||||
video.goFullScreen();
|
||||
|
|
@ -2658,7 +2658,7 @@ class Video extends Component {
|
|||
{
|
||||
var fSuccess = false;
|
||||
if (this.container) {
|
||||
if (this.doFullScreen) {
|
||||
if (this.container.doFullScreen) {
|
||||
/*
|
||||
* Styling the container with a width of "100%" and a height of "auto" works great when the aspect ratio
|
||||
* of our virtual screen is at least roughly equivalent to the physical screen's aspect ratio, but now that
|
||||
|
|
@ -2709,7 +2709,7 @@ class Video extends Component {
|
|||
this.canvasScreen.style.margin = "auto";
|
||||
}
|
||||
this.container.style.backgroundColor = this.colorScreen;
|
||||
this.doFullScreen();
|
||||
this.container.doFullScreen();
|
||||
fSuccess = true;
|
||||
}
|
||||
this.setFocus();
|
||||
|
|
|
|||
|
|
@ -3777,140 +3777,141 @@ class X86CPU extends CPU {
|
|||
popWord()
|
||||
{
|
||||
var w = this.getWord(this.regLSP);
|
||||
|
||||
this.regLSP = (this.regLSP + (I386? this.sizeData : 2))|0;
|
||||
|
||||
/*
|
||||
* Properly comparing regLSP to regLSPLimit would normally require coercing both to unsigned
|
||||
* (ie, floating-point) values. But instead, we do a subtraction, (regLSPLimit - regLSP), and
|
||||
* if the result is negative, we need only be concerned if the signs of both numbers are the same
|
||||
* (ie, the sign of their XOR'ed union is positive).
|
||||
*
|
||||
* TODO: I'm combining the old 8088 address-wrap check with the new segment-limit check,
|
||||
* even though the correct time to do the latter is immediately BEFORE the fetch, not AFTER;
|
||||
* I'm working around this for now by applying a -1 fudge factor to the fault check below.
|
||||
* Comparing regLSP to regLSPLimit requires coercing both to unsigned (ie, floating-point) values.
|
||||
* If we didn't, when we subtracted a 32-bit value like 0x1 from a 32-bit limit like 0xFFFFFFF0 (-16),
|
||||
* we would have a negative result, even though the value was well below the limit.
|
||||
*/
|
||||
var off = ((this.regLSPLimit - this.regLSP)|0);
|
||||
if (off < 0 && (this.regLSPLimit ^ this.regLSP) >= 0) {
|
||||
var delta = (this.regLSPLimit >>> 0) - (this.regLSP >>> 0);
|
||||
if (delta < 0) {
|
||||
/*
|
||||
* There's no such thing as an SS fault on the 8086/8088, and I'm assuming that, on newer
|
||||
* processors, when the stack segment limit is set to the maximum, it's OK for the stack to wrap.
|
||||
* There's no such thing as an SS fault on the 8086/8088, and in fact, we have to support the
|
||||
* operation even when the address straddles the wrap boundary; other emulators tend to barf on
|
||||
* a wrap, usually because they're running in V86 mode instead of real mode.
|
||||
*/
|
||||
if (this.model <= X86.MODEL_8088 || !this.segSS.fExpDown && this.segSS.limit == this.segSS.maskAddr || this.segSS.fExpDown && !this.segSS.limit) {
|
||||
if (this.model <= X86.MODEL_8088) {
|
||||
this.setSP((this.regLSP - this.segSS.base) & this.segSS.maskAddr);
|
||||
} else if (off < -1) { // fudge factor
|
||||
X86.helpFault.call(this, X86.EXCEPTION.SS_FAULT, 0);
|
||||
if (delta < -1) {
|
||||
w = (w & 0xff) | (this.getByte(this.regLSP - 1) << 8);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* I'm assuming that, on newer processors, when the stack segment limit is set to the maximum,
|
||||
* it's OK for the stack to wrap, unless the new address is straddling the wrap boundary (ie, when
|
||||
* delta is < 0 and > -sizeData).
|
||||
*/
|
||||
if (!this.segSS.fExpDown && this.segSS.limit == this.segSS.maskAddr || this.segSS.fExpDown && !this.segSS.limit) {
|
||||
this.setSP((this.regLSP - this.segSS.base) & this.segSS.maskAddr);
|
||||
} else {
|
||||
/*
|
||||
* TODO: I'm combining the old 8088 address-wrap check with the new segment-limit check, even though the
|
||||
* correct time to do the latter is immediately BEFORE the fetch, not AFTER; I'm working around this for now
|
||||
* by applying a -1 fudge factor to the following fault check.
|
||||
*/
|
||||
if (delta < -1) {
|
||||
X86.helpFault.call(this, X86.EXCEPTION.SS_FAULT, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
/**
|
||||
* pushData(d, width, size)
|
||||
*
|
||||
* This function serves two very limited purposes: 1) the ability to push data according to a previous
|
||||
* operand size (width), and 2) the ability to write fewer bytes than the width if necessary (size).
|
||||
*
|
||||
* The former occurs when a 32-bit code segment performs a 16:32 call to a 16-bit code segment; after the
|
||||
* new 16-bit code segment is loaded (and possible stack switch occurs), the return address (both segment
|
||||
* and offset) must still be pushed as 32-bit values.
|
||||
*
|
||||
* The latter occurs with segment register pushes. When a 32-bit operand size is in effect (ie, width is 4),
|
||||
* only the low 16 bits should be written (size must be 2). For all other kinds of pushes, width and size are
|
||||
* impliedly the same.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} d is the data to push at current SP; SP decreased by size
|
||||
* @param {number} width is the width of the data to push, in bytes (must be either 2 or 4)
|
||||
* @param {number} size is the size of the data to push, in bytes (must be 1, 2, or 4, and <= width)
|
||||
*/
|
||||
pushData(d, width, size)
|
||||
{
|
||||
this.assert((width == 2 || width == 4) && (size > 0 && size <= width));
|
||||
|
||||
var regLSP = (this.regLSP - width)|0;
|
||||
|
||||
/*
|
||||
* Properly comparing regLSP to regLSPLimitLow would normally require coercing both to unsigned
|
||||
* (ie, floating-point) values. But instead, we do a subtraction, (regLSP - regLSPLimitLow), and
|
||||
* if the result is negative, we need only be concerned if the signs of both numbers are the same
|
||||
* (ie, the sign of their XOR'ed union is positive).
|
||||
*/
|
||||
if (((regLSP - this.regLSPLimitLow)|0) < 0 && (this.regLSPLimitLow ^ regLSP) >= 0) {
|
||||
/*
|
||||
* There's no such thing as an SS fault on the 8086/8088, and I'm assuming that, on newer
|
||||
* processors, when the stack segment limit is set to the maximum, it's OK for the stack to wrap.
|
||||
*/
|
||||
if (this.model <= X86.MODEL_8088 || !this.segSS.fExpDown && this.segSS.limit == this.segSS.maskAddr || this.segSS.fExpDown && !this.segSS.limit) {
|
||||
this.setSP((regLSP - this.segSS.base) & this.segSS.maskAddr);
|
||||
regLSP = this.regLSP;
|
||||
} else {
|
||||
X86.helpFault.call(this, X86.EXCEPTION.SS_FAULT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
switch(size) {
|
||||
case 1:
|
||||
this.setByte(regLSP, d);
|
||||
break;
|
||||
case 2:
|
||||
this.setShort(regLSP, d);
|
||||
break;
|
||||
case 4:
|
||||
this.setLong(regLSP, d);
|
||||
break;
|
||||
default:
|
||||
this.assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* We update this.regLSP at the end to make life simpler for opcode handlers that perform only one
|
||||
* pushWord() operation, relieving them from having to snapshot this.regLSP into this.opLSP needlessly.
|
||||
*/
|
||||
this.regLSP = regLSP;
|
||||
}
|
||||
|
||||
/**
|
||||
* pushWord(w)
|
||||
*
|
||||
* NOTE: pushWord() used to do a simplified version of pushData(), and while that might have made the emulator
|
||||
* slightly faster, it was woefully duplicative. Let's trust the combination of the Closure Compiler and the
|
||||
* JavaScript engines to automatically inline instead.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} w is the word (16-bit) value to push at current SP; SP decreased by 2 or 4
|
||||
*/
|
||||
pushWord(w)
|
||||
{
|
||||
/*
|
||||
* This assertion is no longer valid, now that we've fixed opPUSH8() to use getIPDisp() instead of getIPByte(),
|
||||
* thus sign-extending the byte as appropriate. And since sign-extension necessarily affects the entire 32-bit
|
||||
* value, this assertion could fail when dataMask is 16 bits.
|
||||
*
|
||||
* this.assert((w & this.maskData) == w);
|
||||
*
|
||||
* setWord() calls setShort() or setLong() as appropriate, and setShort() truncates incoming values, so the fact
|
||||
* that any incoming signed values will not be truncated to 16 bits should not be a concern.
|
||||
*/
|
||||
var regLSP = (this.regLSP - (I386? this.sizeData : 2))|0;
|
||||
this.pushData(w, I386? this.sizeData : 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* pushData(data, width, size)
|
||||
*
|
||||
* The size parameter serves two very limited purposes: 1) the ability to push data according to a previous
|
||||
* operand size, and 2) the ability to write fewer bytes than the width if necessary.
|
||||
*
|
||||
* The former occurs when a 32-bit code segment performs a 16:32 call to a 16-bit code segment; after the
|
||||
* new 16-bit code segment is loaded (and possible stack switch occurs), the return address (both segment
|
||||
* and offset) must still be pushed as 32-bit values.
|
||||
*
|
||||
* The latter occurs with segment register pushes. When a 32-bit operand size is in effect (ie, width is 4),
|
||||
* only the low 16 bits should be written (size must be 2).
|
||||
*
|
||||
* For all other kinds of pushes, width and size are impliedly the same.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} data is the data to push at current SP; SP decreased by size
|
||||
* @param {number} width is the width of the data to push, in bytes (must be either 2 or 4)
|
||||
* @param {number} [size] is the size of the data to push, in bytes (must be 1, 2, or 4, and <= width)
|
||||
*/
|
||||
pushData(data, width, size = width)
|
||||
{
|
||||
this.assert((width == 2 || width == 4) && (size > 0 && size <= width));
|
||||
|
||||
var regLSP = (this.regLSP - width)|0;
|
||||
/*
|
||||
* Properly comparing regLSP to regLSPLimitLow would normally require coercing both to unsigned
|
||||
* (ie, floating-point) values. But instead, we do a subtraction, (regLSP - regLSPLimitLow), and
|
||||
* if the result is negative, we need only be concerned if the signs of both numbers are the same
|
||||
* (ie, the sign of their XOR'ed union is positive).
|
||||
* Comparing regLSP to regLSPLimitLow requires coercing both to unsigned (ie, floating-point) values.
|
||||
* If we didn't, when we subtracted a 32-bit limit like 0xFFFFFFF0 (-16) from a 32-bit value like 0x1,
|
||||
* we would have a positive result, even though the value was well below the limit.
|
||||
*/
|
||||
if (((regLSP - this.regLSPLimitLow)|0) < 0 && (this.regLSPLimitLow ^ regLSP) >= 0) {
|
||||
var delta = (regLSP >>> 0) - (this.regLSPLimitLow >>> 0);
|
||||
if (delta < 0) {
|
||||
/*
|
||||
* There's no such thing as an SS fault on the 8086/8088, and I'm assuming that, on newer
|
||||
* processors, when the stack segment limit is set to the maximum, it's OK for the stack to wrap.
|
||||
* There's no such thing as an SS fault on the 8086/8088, and in fact, we have to support the
|
||||
* operation even when the address straddles the wrap boundary (ie, when delta is -1); other
|
||||
* emulators tend to barf on a wrap, usually because they're running in V86 mode instead of real mode.
|
||||
*/
|
||||
if (this.model <= X86.MODEL_8088 || !this.segSS.fExpDown && this.segSS.limit == this.segSS.maskAddr || this.segSS.fExpDown && !this.segSS.limit) {
|
||||
if (this.model <= X86.MODEL_8088) {
|
||||
if (delta == -1) {
|
||||
this.setByte(regLSP + 1, data >> 8);
|
||||
this.setSP((regLSP - this.segSS.base) & this.segSS.maskAddr);
|
||||
this.setByte(this.regLSP, data);
|
||||
return;
|
||||
}
|
||||
this.assert(!this.segSS.fExpDown && this.segSS.limit == this.segSS.maskAddr);
|
||||
}
|
||||
/*
|
||||
* I'm assuming that, on newer processors, when the stack segment limit is set to the maximum,
|
||||
* it's OK for the stack to wrap, unless the new address is straddling the wrap boundary (ie, when
|
||||
* delta is < 0 and > -width).
|
||||
*/
|
||||
if (!this.segSS.fExpDown && this.segSS.limit == this.segSS.maskAddr || this.segSS.fExpDown && !this.segSS.limit) {
|
||||
if (delta < 0 && delta > -width) {
|
||||
X86.helpFault.call(this, X86.EXCEPTION.SS_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
this.setSP((regLSP - this.segSS.base) & this.segSS.maskAddr);
|
||||
regLSP = this.regLSP;
|
||||
} else {
|
||||
X86.helpFault.call(this, X86.EXCEPTION.SS_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.setWord(regLSP, w);
|
||||
switch(size) {
|
||||
case 1:
|
||||
this.setByte(regLSP, data);
|
||||
break;
|
||||
case 2:
|
||||
this.setShort(regLSP, data);
|
||||
break;
|
||||
case 4:
|
||||
this.setLong(regLSP, data);
|
||||
break;
|
||||
default:
|
||||
this.assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* We update this.regLSP at the end to make life simpler for opcode handlers that perform only one
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -12146,11 +12146,20 @@ class CPU extends Component {
|
|||
case "run":
|
||||
this.bindings[sBinding] = control;
|
||||
control.onclick = function onClickRun() {
|
||||
var fRunning = cpu.flags.running;
|
||||
if (!cpu.cmp || !cpu.cmp.checkPower()) return;
|
||||
if (!cpu.flags.running)
|
||||
cpu.runCPU(true);
|
||||
else
|
||||
cpu.stopCPU(true);
|
||||
/*
|
||||
* We snapped the CPU's running flag before calling checkPower() because there are rare (REPOWER)
|
||||
* situations where checkPower() will have started the CPU as well. So toggle the CPU state ONLY
|
||||
* if the running flag remains unchanged.
|
||||
*/
|
||||
if (fRunning == cpu.flags.running) {
|
||||
if (!cpu.flags.running) {
|
||||
cpu.runCPU(true);
|
||||
} else {
|
||||
cpu.stopCPU(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
fBound = true;
|
||||
break;
|
||||
|
|
@ -18237,140 +18246,141 @@ class X86CPU extends CPU {
|
|||
popWord()
|
||||
{
|
||||
var w = this.getWord(this.regLSP);
|
||||
|
||||
this.regLSP = (this.regLSP + (I386? this.sizeData : 2))|0;
|
||||
|
||||
/*
|
||||
* Properly comparing regLSP to regLSPLimit would normally require coercing both to unsigned
|
||||
* (ie, floating-point) values. But instead, we do a subtraction, (regLSPLimit - regLSP), and
|
||||
* if the result is negative, we need only be concerned if the signs of both numbers are the same
|
||||
* (ie, the sign of their XOR'ed union is positive).
|
||||
*
|
||||
* TODO: I'm combining the old 8088 address-wrap check with the new segment-limit check,
|
||||
* even though the correct time to do the latter is immediately BEFORE the fetch, not AFTER;
|
||||
* I'm working around this for now by applying a -1 fudge factor to the fault check below.
|
||||
* Comparing regLSP to regLSPLimit requires coercing both to unsigned (ie, floating-point) values.
|
||||
* If we didn't, when we subtracted a 32-bit value like 0x1 from a 32-bit limit like 0xFFFFFFF0 (-16),
|
||||
* we would have a negative result, even though the value was well below the limit.
|
||||
*/
|
||||
var off = ((this.regLSPLimit - this.regLSP)|0);
|
||||
if (off < 0 && (this.regLSPLimit ^ this.regLSP) >= 0) {
|
||||
var delta = (this.regLSPLimit >>> 0) - (this.regLSP >>> 0);
|
||||
if (delta < 0) {
|
||||
/*
|
||||
* There's no such thing as an SS fault on the 8086/8088, and I'm assuming that, on newer
|
||||
* processors, when the stack segment limit is set to the maximum, it's OK for the stack to wrap.
|
||||
* There's no such thing as an SS fault on the 8086/8088, and in fact, we have to support the
|
||||
* operation even when the address straddles the wrap boundary; other emulators tend to barf on
|
||||
* a wrap, usually because they're running in V86 mode instead of real mode.
|
||||
*/
|
||||
if (this.model <= X86.MODEL_8088 || !this.segSS.fExpDown && this.segSS.limit == this.segSS.maskAddr || this.segSS.fExpDown && !this.segSS.limit) {
|
||||
if (this.model <= X86.MODEL_8088) {
|
||||
this.setSP((this.regLSP - this.segSS.base) & this.segSS.maskAddr);
|
||||
} else if (off < -1) { // fudge factor
|
||||
X86.helpFault.call(this, X86.EXCEPTION.SS_FAULT, 0);
|
||||
if (delta < -1) {
|
||||
w = (w & 0xff) | (this.getByte(this.regLSP - 1) << 8);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* I'm assuming that, on newer processors, when the stack segment limit is set to the maximum,
|
||||
* it's OK for the stack to wrap, unless the new address is straddling the wrap boundary (ie, when
|
||||
* delta is < 0 and > -sizeData).
|
||||
*/
|
||||
if (!this.segSS.fExpDown && this.segSS.limit == this.segSS.maskAddr || this.segSS.fExpDown && !this.segSS.limit) {
|
||||
this.setSP((this.regLSP - this.segSS.base) & this.segSS.maskAddr);
|
||||
} else {
|
||||
/*
|
||||
* TODO: I'm combining the old 8088 address-wrap check with the new segment-limit check, even though the
|
||||
* correct time to do the latter is immediately BEFORE the fetch, not AFTER; I'm working around this for now
|
||||
* by applying a -1 fudge factor to the following fault check.
|
||||
*/
|
||||
if (delta < -1) {
|
||||
X86.helpFault.call(this, X86.EXCEPTION.SS_FAULT, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
/**
|
||||
* pushData(d, width, size)
|
||||
*
|
||||
* This function serves two very limited purposes: 1) the ability to push data according to a previous
|
||||
* operand size (width), and 2) the ability to write fewer bytes than the width if necessary (size).
|
||||
*
|
||||
* The former occurs when a 32-bit code segment performs a 16:32 call to a 16-bit code segment; after the
|
||||
* new 16-bit code segment is loaded (and possible stack switch occurs), the return address (both segment
|
||||
* and offset) must still be pushed as 32-bit values.
|
||||
*
|
||||
* The latter occurs with segment register pushes. When a 32-bit operand size is in effect (ie, width is 4),
|
||||
* only the low 16 bits should be written (size must be 2). For all other kinds of pushes, width and size are
|
||||
* impliedly the same.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} d is the data to push at current SP; SP decreased by size
|
||||
* @param {number} width is the width of the data to push, in bytes (must be either 2 or 4)
|
||||
* @param {number} size is the size of the data to push, in bytes (must be 1, 2, or 4, and <= width)
|
||||
*/
|
||||
pushData(d, width, size)
|
||||
{
|
||||
|
||||
|
||||
var regLSP = (this.regLSP - width)|0;
|
||||
|
||||
/*
|
||||
* Properly comparing regLSP to regLSPLimitLow would normally require coercing both to unsigned
|
||||
* (ie, floating-point) values. But instead, we do a subtraction, (regLSP - regLSPLimitLow), and
|
||||
* if the result is negative, we need only be concerned if the signs of both numbers are the same
|
||||
* (ie, the sign of their XOR'ed union is positive).
|
||||
*/
|
||||
if (((regLSP - this.regLSPLimitLow)|0) < 0 && (this.regLSPLimitLow ^ regLSP) >= 0) {
|
||||
/*
|
||||
* There's no such thing as an SS fault on the 8086/8088, and I'm assuming that, on newer
|
||||
* processors, when the stack segment limit is set to the maximum, it's OK for the stack to wrap.
|
||||
*/
|
||||
if (this.model <= X86.MODEL_8088 || !this.segSS.fExpDown && this.segSS.limit == this.segSS.maskAddr || this.segSS.fExpDown && !this.segSS.limit) {
|
||||
this.setSP((regLSP - this.segSS.base) & this.segSS.maskAddr);
|
||||
regLSP = this.regLSP;
|
||||
} else {
|
||||
X86.helpFault.call(this, X86.EXCEPTION.SS_FAULT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
switch(size) {
|
||||
case 1:
|
||||
this.setByte(regLSP, d);
|
||||
break;
|
||||
case 2:
|
||||
this.setShort(regLSP, d);
|
||||
break;
|
||||
case 4:
|
||||
this.setLong(regLSP, d);
|
||||
break;
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* We update this.regLSP at the end to make life simpler for opcode handlers that perform only one
|
||||
* pushWord() operation, relieving them from having to snapshot this.regLSP into this.opLSP needlessly.
|
||||
*/
|
||||
this.regLSP = regLSP;
|
||||
}
|
||||
|
||||
/**
|
||||
* pushWord(w)
|
||||
*
|
||||
* NOTE: pushWord() used to do a simplified version of pushData(), and while that might have made the emulator
|
||||
* slightly faster, it was woefully duplicative. Let's trust the combination of the Closure Compiler and the
|
||||
* JavaScript engines to automatically inline instead.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} w is the word (16-bit) value to push at current SP; SP decreased by 2 or 4
|
||||
*/
|
||||
pushWord(w)
|
||||
{
|
||||
/*
|
||||
* This assertion is no longer valid, now that we've fixed opPUSH8() to use getIPDisp() instead of getIPByte(),
|
||||
* thus sign-extending the byte as appropriate. And since sign-extension necessarily affects the entire 32-bit
|
||||
* value, this assertion could fail when dataMask is 16 bits.
|
||||
*
|
||||
*
|
||||
*
|
||||
* setWord() calls setShort() or setLong() as appropriate, and setShort() truncates incoming values, so the fact
|
||||
* that any incoming signed values will not be truncated to 16 bits should not be a concern.
|
||||
*/
|
||||
var regLSP = (this.regLSP - (I386? this.sizeData : 2))|0;
|
||||
this.pushData(w, I386? this.sizeData : 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* pushData(data, width, size)
|
||||
*
|
||||
* The size parameter serves two very limited purposes: 1) the ability to push data according to a previous
|
||||
* operand size, and 2) the ability to write fewer bytes than the width if necessary.
|
||||
*
|
||||
* The former occurs when a 32-bit code segment performs a 16:32 call to a 16-bit code segment; after the
|
||||
* new 16-bit code segment is loaded (and possible stack switch occurs), the return address (both segment
|
||||
* and offset) must still be pushed as 32-bit values.
|
||||
*
|
||||
* The latter occurs with segment register pushes. When a 32-bit operand size is in effect (ie, width is 4),
|
||||
* only the low 16 bits should be written (size must be 2).
|
||||
*
|
||||
* For all other kinds of pushes, width and size are impliedly the same.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} data is the data to push at current SP; SP decreased by size
|
||||
* @param {number} width is the width of the data to push, in bytes (must be either 2 or 4)
|
||||
* @param {number} [size] is the size of the data to push, in bytes (must be 1, 2, or 4, and <= width)
|
||||
*/
|
||||
pushData(data, width, size = width)
|
||||
{
|
||||
|
||||
|
||||
var regLSP = (this.regLSP - width)|0;
|
||||
/*
|
||||
* Properly comparing regLSP to regLSPLimitLow would normally require coercing both to unsigned
|
||||
* (ie, floating-point) values. But instead, we do a subtraction, (regLSP - regLSPLimitLow), and
|
||||
* if the result is negative, we need only be concerned if the signs of both numbers are the same
|
||||
* (ie, the sign of their XOR'ed union is positive).
|
||||
* Comparing regLSP to regLSPLimitLow requires coercing both to unsigned (ie, floating-point) values.
|
||||
* If we didn't, when we subtracted a 32-bit limit like 0xFFFFFFF0 (-16) from a 32-bit value like 0x1,
|
||||
* we would have a positive result, even though the value was well below the limit.
|
||||
*/
|
||||
if (((regLSP - this.regLSPLimitLow)|0) < 0 && (this.regLSPLimitLow ^ regLSP) >= 0) {
|
||||
var delta = (regLSP >>> 0) - (this.regLSPLimitLow >>> 0);
|
||||
if (delta < 0) {
|
||||
/*
|
||||
* There's no such thing as an SS fault on the 8086/8088, and I'm assuming that, on newer
|
||||
* processors, when the stack segment limit is set to the maximum, it's OK for the stack to wrap.
|
||||
* There's no such thing as an SS fault on the 8086/8088, and in fact, we have to support the
|
||||
* operation even when the address straddles the wrap boundary (ie, when delta is -1); other
|
||||
* emulators tend to barf on a wrap, usually because they're running in V86 mode instead of real mode.
|
||||
*/
|
||||
if (this.model <= X86.MODEL_8088 || !this.segSS.fExpDown && this.segSS.limit == this.segSS.maskAddr || this.segSS.fExpDown && !this.segSS.limit) {
|
||||
if (this.model <= X86.MODEL_8088) {
|
||||
if (delta == -1) {
|
||||
this.setByte(regLSP + 1, data >> 8);
|
||||
this.setSP((regLSP - this.segSS.base) & this.segSS.maskAddr);
|
||||
this.setByte(this.regLSP, data);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* I'm assuming that, on newer processors, when the stack segment limit is set to the maximum,
|
||||
* it's OK for the stack to wrap, unless the new address is straddling the wrap boundary (ie, when
|
||||
* delta is < 0 and > -width).
|
||||
*/
|
||||
if (!this.segSS.fExpDown && this.segSS.limit == this.segSS.maskAddr || this.segSS.fExpDown && !this.segSS.limit) {
|
||||
if (delta < 0 && delta > -width) {
|
||||
X86.helpFault.call(this, X86.EXCEPTION.SS_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
this.setSP((regLSP - this.segSS.base) & this.segSS.maskAddr);
|
||||
regLSP = this.regLSP;
|
||||
} else {
|
||||
X86.helpFault.call(this, X86.EXCEPTION.SS_FAULT, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.setWord(regLSP, w);
|
||||
switch(size) {
|
||||
case 1:
|
||||
this.setByte(regLSP, data);
|
||||
break;
|
||||
case 2:
|
||||
this.setShort(regLSP, data);
|
||||
break;
|
||||
case 4:
|
||||
this.setLong(regLSP, data);
|
||||
break;
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* We update this.regLSP at the end to make life simpler for opcode handlers that perform only one
|
||||
|
|
@ -48883,10 +48893,10 @@ class Video extends Component {
|
|||
* is exasperating; browsers can't agree on 'full' or 'Full, 'request' or 'Request', 'screen' or 'Screen', and
|
||||
* while some browsers honor other browser prefixes, most browsers don't.
|
||||
*/
|
||||
this.doFullScreen = null;
|
||||
if ((this.container = container)) {
|
||||
this.doFullScreen = container['requestFullscreen'] || container['msRequestFullscreen'] || container['mozRequestFullScreen'] || container['webkitRequestFullscreen'];
|
||||
if (this.doFullScreen) {
|
||||
this.container = container;
|
||||
if (this.container) {
|
||||
this.container.doFullScreen = container['requestFullscreen'] || container['msRequestFullscreen'] || container['mozRequestFullScreen'] || container['webkitRequestFullscreen'];
|
||||
if (this.container.doFullScreen) {
|
||||
for (i = 0; i < asWebPrefixes.length; i++) {
|
||||
sEvent = asWebPrefixes[i] + 'fullscreenchange';
|
||||
if ('on' + sEvent in document) {
|
||||
|
|
@ -49102,7 +49112,7 @@ class Video extends Component {
|
|||
switch (sBinding) {
|
||||
|
||||
case "fullScreen":
|
||||
if (this.doFullScreen) {
|
||||
if (this.container && this.container.doFullScreen) {
|
||||
control.onclick = function onClickFullScreen() {
|
||||
if (DEBUG) video.printMessage("fullScreen()");
|
||||
video.goFullScreen();
|
||||
|
|
@ -49188,7 +49198,7 @@ class Video extends Component {
|
|||
{
|
||||
var fSuccess = false;
|
||||
if (this.container) {
|
||||
if (this.doFullScreen) {
|
||||
if (this.container.doFullScreen) {
|
||||
/*
|
||||
* Styling the container with a width of "100%" and a height of "auto" works great when the aspect ratio
|
||||
* of our virtual screen is at least roughly equivalent to the physical screen's aspect ratio, but now that
|
||||
|
|
@ -49239,7 +49249,7 @@ class Video extends Component {
|
|||
this.canvasScreen.style.margin = "auto";
|
||||
}
|
||||
this.container.style.backgroundColor = this.colorScreen;
|
||||
this.doFullScreen();
|
||||
this.container.doFullScreen();
|
||||
fSuccess = true;
|
||||
}
|
||||
this.setFocus();
|
||||
|
|
@ -75087,6 +75097,28 @@ class Computer extends Component {
|
|||
*/
|
||||
checkPower()
|
||||
{
|
||||
if (this.flags.unloading) {
|
||||
/*
|
||||
* We happen to know that we're currently only called by the CPU's onClickRun() function, so
|
||||
* if the unloading flag is set, then we've somehow gotten into a weird state where the machine
|
||||
* thinks it's being (or has been) unloaded by the browser, but in fact, it has not.
|
||||
*
|
||||
* The only time I've seen this happen is when the user clicks a link on a page that the browser
|
||||
* decided to treat as a download operation, instead of loading a new page. The proper way to
|
||||
* resolve that confusion is to set the "download" attribute on the link (which will prevent the
|
||||
* page's "onbeforeunload" handler from being called in the first place), but we cannot guarantee
|
||||
* that all such links will have their "download" attribute properly set.
|
||||
*
|
||||
* Hence, this code: we do the same thing that the show() function does, which is to attempt a
|
||||
* REPOWER operation. If that doesn't result in the powered flag getting turned back on, well,
|
||||
* then we're probably screwed.
|
||||
*/
|
||||
this.flags.unloading = false;
|
||||
if (this.flags.initDone && !this.flags.powered) {
|
||||
this.powerOn(Computer.RESUME_REPOWER);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.flags.powered) return true;
|
||||
|
||||
var component = null, iComponent;
|
||||
|
|
@ -75897,6 +75929,9 @@ class Computer extends Component {
|
|||
var computer = /** @type {Computer} */ (Component.getComponentByType("Computer", parmsComputer['id']));
|
||||
if (computer) {
|
||||
|
||||
/*
|
||||
* Clear new flag that Component functions (eg, notice()) should check before alerting the user.
|
||||
*/
|
||||
computer.flags.unloading = false;
|
||||
|
||||
if (DEBUG && computer.messageEnabled()) {
|
||||
|
|
@ -75904,7 +75939,7 @@ class Computer extends Component {
|
|||
}
|
||||
|
||||
if (computer.flags.initDone && !computer.flags.powered) {
|
||||
/**
|
||||
/*
|
||||
* Repower the computer, notifying every component to continue running as-is.
|
||||
*/
|
||||
computer.powerOn(Computer.RESUME_REPOWER);
|
||||
|
|
@ -75949,7 +75984,7 @@ class Computer extends Component {
|
|||
if (computer) {
|
||||
|
||||
/*
|
||||
* Added a new flag that Component functions (eg, notice()) should check before alerting the user.
|
||||
* Set new flag that Component functions (eg, notice()) should check before alerting the user.
|
||||
*/
|
||||
computer.flags.unloading = true;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue