Improved separation of FDC media parameters from drive parameters

This commit is contained in:
Jeff Parsons 2014-10-20 00:24:13 -05:00 committed by jeffpar
commit 1809543577
32 changed files with 1915 additions and 1887 deletions

View file

@ -36,17 +36,17 @@
"use strict";
var fs = require("fs");
var path = require("path");
var http = require("http");
var mkdirp = require("mkdirp");
var crypto = require("crypto");
var net = require("../../shared/lib/netlib");
var proc = require("../../shared/lib/proclib");
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var fs = require("fs");
var path = require("path");
var http = require("http");
var mkdirp = require("mkdirp");
var crypto = require("crypto");
var net = require("../../shared/lib/netlib");
var proc = require("../../shared/lib/proclib");
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var DumpAPI = require("../../shared/lib/dumpapi");
var X86 = require("../../pcjs-client/lib/x86");
var X86 = require("../../pcjs-client/lib/x86");
/**
* @class exports
@ -73,7 +73,7 @@ var fNormalize = true;
*
* TODO: Honor the caller's mbHD size. At the moment, any hard disk build request translates to 10Mb,
* since we rely on a "canned" BPB in aDefaultBPBs.
*
*
* TODO: If sServerRoot is set, make sure the final sDiskPath refers to something in either /apps/ or /disks/,
* to prevent random enumeration of other server resources.
*
@ -114,7 +114,7 @@ function DiskDump(sDiskPath, asExclude, sFormat, fComments, mbHD, sServerRoot, s
/*
* If we have to enumerate one or more files during the buildImage() process, this array
* will save them, in case the caller wants to query that information later, in updateManifest().
*
*
* Originally, I thought each saved entry would be a subset of what the fileInfo objects contain,
* but it turns out I pretty much need everything. This, in turn, means that some of the original
* buildImage() functions could simply use this.aManifestInfo, instead of their own aFiles array,
@ -196,7 +196,7 @@ DiskDump.MY_OEM_STRING = "PCJS.ORG";
/**
* The BPBs that buildImage() currently supports; these BPBs should be in order of smallest to largest capacity,
* to help insure we don't select a disk format larger than necessary.
* to help insure we don't select a disk format larger than necessary.
*/
DiskDump.aDefaultBPBs = [
[ // define BPB for 160Kb diskette
@ -279,7 +279,7 @@ DiskDump.aDefaultBPBs = [
* diskdump --dir={directory} [--format=json|data|hex|bytes|img] [--comments] [--output={file}]
* diskdump --disk={disk image} [--format=json|data|hex|bytes|img] [--comments] [--output={file}]
* diskdump --path={file[;file]...} [--format=json|data|hex|bytes|img] [--comments] [--output={file}]
*
*
* NOTE: --img is permitted as an alias for --disk
*
* Arguments
@ -290,9 +290,9 @@ DiskDump.aDefaultBPBs = [
*
* Note that command-line arguments, if any, are not validated. For example, argv['comments'] may be any of
* boolean, string, or undefined, since the user may have typed "--comments" or "--comments=foo" or nothing at all.
*
*
* Additional command-line arguments include:
*
*
* --mbhd={number}: requests a hard disk image with the given number of megabytes (eg, 10 for a 10mb image)
* --exclude={filename}: specifies a filename that should be excluded from the image; repeat as often as needed
* --overwrite: allows the --output option to overwrite an existing file; default is to NOT overwrite
@ -320,7 +320,7 @@ DiskDump.CLI = function()
var sDiskPath = null, sServerRoot = "";
var sDir = argv['dir'], sDisk = (argv['disk'] || argv['img']), sPath = argv['path'];
if (typeof sDir == "string") {
sDiskPath = sDir;
}
@ -345,7 +345,7 @@ DiskDump.CLI = function()
if (sManifestFile && sManifestFile.charAt(0) != '/') {
sManifestFile = path.join(process.cwd(), sManifestFile);
}
var sOutput = "";
var sOutputFile = argv['output'];
if (typeof sOutputFile == "string" && !str.endsWith(sOutputFile, ".img") && !str.endsWith(sOutputFile, ".json")) {
@ -362,14 +362,14 @@ DiskDump.CLI = function()
}
} else {
sOutput = "disk";
}
}
sOutputFile = sOutput + '.' + argv['format'];
}
if (sOutputFile && sOutputFile.charAt(0) != '/') sOutputFile = path.join(process.cwd(), sOutputFile);
var fOverwrite = argv['overwrite'];
var sManifestTitle = argv['title'];
if (sDiskPath) {
var disk = new DiskDump(sDiskPath, asExclude, argv['format'], argv['comments'], argv['mbhd'], sServerRoot, sManifestFile);
if (sDir) {
@ -394,7 +394,7 @@ DiskDump.CLI = function()
else {
console.log("usage: diskdump --dir={dir}|--disk={disk}|--path={file}[;{file}...] [--format=json|data|hex|bytes|img] [--comments] [--output={file}] [--manifest={file}]");
}
if (err) {
DiskDump.logError(err);
process.exit(1);
@ -432,9 +432,9 @@ DiskDump.outputDisk = function(err, disk, sDiskPath, sOutputFile, fOverwrite, sM
}
}
if (data) {
var cbDisk = (disk.bufDisk? disk.bufDisk.length : data.length);
if (sOutputFile) {
var fUnchanged;
@ -448,7 +448,7 @@ DiskDump.outputDisk = function(err, disk, sDiskPath, sOutputFile, fOverwrite, sM
}
fUnchanged = DiskDump.updateManifest(disk, disk.sManifestFile, sDiskPath, sOutputFile, true, sManifestTitle, md5Disk, md5JSON);
}
try {
if (fUnchanged) {
console.log(sOutputFile + " unchanged");
@ -468,7 +468,7 @@ DiskDump.outputDisk = function(err, disk, sDiskPath, sOutputFile, fOverwrite, sM
} else {
/*
* We'll dump JSON to the console, but not a raw disk buffer; we could add an option to
* "stringify" buffers, but if that's what the caller wants, they should use "--format=json".
* "stringify" buffers, but if that's what the caller wants, they should use "--format=json".
*/
if (typeof data == "string") {
console.log(data);
@ -480,7 +480,7 @@ DiskDump.outputDisk = function(err, disk, sDiskPath, sOutputFile, fOverwrite, sM
err = new Error("unable to convert " + disk.sDiskPath);
}
}
if (err) {
DiskDump.logError(err);
process.exit(1);
@ -489,7 +489,7 @@ DiskDump.outputDisk = function(err, disk, sDiskPath, sOutputFile, fOverwrite, sM
/**
* getManifestAttr(sID, sTag)
*
*
* @param sID
* @param sTag
* @return {string|null}
@ -503,14 +503,14 @@ DiskDump.getManifestAttr = function(sID, sTag)
/**
* updateManifest(disk, sManifestFile, sDiskPath, sOutputFile, fOverwrite, sTitle, md5Disk, md5JSON)
*
*
* This function reports a change if EITHER the md5Disk value does not match the original
* "md5" value recorded in the manifest OR the manifest itself has changed. If md5JSON is
* also provided, we require that to match as well.
*
*
* Since this function is for command-line use only, we use *Sync functions, so that we can
* return the results immediately.
*
*
* @param {DiskDump} disk
* @param {string} sManifestFile
* @param {string} sDiskPath
@ -523,9 +523,9 @@ DiskDump.getManifestAttr = function(sID, sTag)
*/
DiskDump.updateManifest = function(disk, sManifestFile, sDiskPath, sOutputFile, fOverwrite, sTitle, md5Disk, md5JSON)
{
var fUnchanged, fExists = false, sXML, err = null;
var fUnchanged, fExists = false, sXML, err = null;
var sMatchDisk = null, sIDDisk = null, sMD5Disk = null, sMD5JSON = null;
try {
sXML = fs.readFileSync(sManifestFile, {encoding: "utf8"});
fExists = true;
@ -544,7 +544,7 @@ DiskDump.updateManifest = function(disk, sManifestFile, sDiskPath, sOutputFile,
sXML += '\t<title' + sPrefix + '>' + sTitle + '</title>\n';
sXML += '</manifest>';
}
var i = sOutputFile.indexOf("/disks/");
if (i > 0) {
sOutputFile = sOutputFile.substr(i);
@ -562,7 +562,7 @@ DiskDump.updateManifest = function(disk, sManifestFile, sDiskPath, sOutputFile,
sMD5Disk = DiskDump.getManifestAttr("md5", match[1]);
sMD5JSON = DiskDump.getManifestAttr("md5json", match[1]);
}
if (!sIDDisk) {
for (i = 1; i < 1000; i++) {
sIDDisk = i.toString();
@ -574,7 +574,7 @@ DiskDump.updateManifest = function(disk, sManifestFile, sDiskPath, sOutputFile,
err = new Error("manifest already contains " + i + " disks");
}
}
if (!err) {
/*
* Thanks to buildImage(), fDir is true if a "dir" parameter was provided, false if a "path" parameter was provided,
@ -586,10 +586,10 @@ DiskDump.updateManifest = function(disk, sManifestFile, sDiskPath, sOutputFile,
} else if (disk.fDir === undefined) {
sParm = "img";
}
/*
* Build a "size" attribute with the total disk size in bytes and a "chs" attribute that describes the disk geometry; eg:
*
*
* size="368640" chs="40:2:9"
*/
var size = 0, sCHS = "";
@ -598,7 +598,7 @@ DiskDump.updateManifest = function(disk, sManifestFile, sDiskPath, sOutputFile,
size = disk.dataDisk.length * disk.dataDisk[0].length * disk.dataDisk[0][0].length * disk.dataDisk[0][0][0].length;
}
var sXMLDisk = '\t<disk id="' + sIDDisk + '"' + (size? ' size="' + size + '"' : '') + (sCHS? ' chs="' + sCHS + '"' : '') + (sParm? ' ' + sParm + '="' + sDiskPath + '"' : '') + ' href="' + sOutputFile + '"' + (md5Disk? ' md5="' + md5Disk + '"' : '') + (md5JSON? ' md5json="' + md5JSON + '"' : '') + '>\n';
var sName = "";
if (sMatchDisk && (match = sMatchDisk.match(/<name>([^>]*)<\/name>/))) {
sName = match[1];
@ -618,7 +618,7 @@ DiskDump.updateManifest = function(disk, sManifestFile, sDiskPath, sOutputFile,
var fileInfo = disk.aManifestInfo[i];
if (fileInfo.FILE_SIZE < 0) continue; // ignore non-file entries
var sDir = path.dirname(fileInfo.FILE_PATH) + '/';
if (sBaseDir === null) sBaseDir = sDir;
if (sBaseDir === null) sBaseDir = sDir;
sAttrs += ' size="' + fileInfo.FILE_SIZE + '"';
sAttrs += ' time="' + usr.formatDate("Y-m-d H:i:s", fileInfo.FILE_TIME) + '"';
sAttrs += ' attr="0x' + fileInfo.FILE_ATTR.toString(16) + '"';
@ -713,7 +713,7 @@ DiskDump.getStat = function(sPath, done)
/**
* readFile(sPath, sEncoding, done)
*
*
* An alternative to fs.readFile() that handles supported remote files, in addition to local files
*
* @param {string} sPath
@ -753,7 +753,7 @@ DiskDump.readFile = function(sPath, sEncoding, done)
*
* @this {DiskDump}
* @param {string} sName is the basename of a file under consideration
* @return {boolean} is true if the file should be excluded, false if not
* @return {boolean} is true if the file should be excluded, false if not
*/
DiskDump.prototype.isExcluded = function(sName)
{
@ -818,7 +818,7 @@ DiskDump.prototype.setData = function(err, buf, done)
* with comments, so if the caller has requested comments, we immediately convert the
* JSON to a Buffer and throw the JSON away. The next convertToJSON() call will take care
* of the rest.
*
*
* We could also move this functionality into its own function, or wait until the
* caller actually calls convertToJSON() -- although if the caller inadvertently calls
* convertToJSON() multiple times, you don't want to be regenerating the JSON every time.
@ -1049,7 +1049,7 @@ DiskDump.ATTR_ARCHIVE = 0x20;
*
* @this {DiskDump}
* @param {Date} dateTime
* @return {boolean} true if date/time modified, false if not
* @return {boolean} true if date/time modified, false if not
*/
DiskDump.prototype.validateTime = function(dateTime)
{
@ -1067,7 +1067,7 @@ DiskDump.prototype.validateTime = function(dateTime)
* nothing that in PC-DOS 2.0, I observed a date with the largest possible year value (127) displayed as
* "12-31-:7" (an ASCII ':' is the next highest character after '0'). While that DOES distinguish the year
* 2007 from the year 2107, we probably shouldn't allow any year > 2099, to eliminate confusion.
*
*
* In fact, it might be worth setting the upper limit to 2079, otherwise a date like "12-31-81" is ambiguous
* (it could mean 1981 or 2081). But I'll stick to a limit of 2099 for now.
*/
@ -1168,19 +1168,19 @@ DiskDump.prototype.readDir = function(sDir, fRoot, done)
done(err, null);
return;
}
/*
* Sorting file names now (since they're just strings) is easier/faster than sorting the filtered
* aFiles array later (which would require the use of a compare function), so we do the sort now; it
* has no bearing on the outcome. Note that the lack of a stable sort in JavaScript also has no
* bearing, because we're sorting on name, and every name is different.
*
*
* However, it's not entirely clear whether this is strictly necessary. I think the variations in
* file name order that I was originally seeing may have simply been due to out-of-order fs.stat()
* calls, because I used to call addManifestInfo() in the callback.
*/
// if (fNormalize) asFiles.sort();
for (iFile = 0; iFile < asFiles.length; iFile++) {
var sFileName = asFiles[iFile];
/*
@ -1201,7 +1201,7 @@ DiskDump.prototype.readDir = function(sDir, fRoot, done)
fileInfo.FILE_NAME = obj.buildName(sFileName);
fileInfo.FILE_PATH = sFilePath;
aFiles.push(fileInfo);
/*
* We add the fileInfo objects to the aManifestInfo array NOW, because the fs.stat() callbacks may
* occur out-of-order. The only downside is that non-file entries can now appear in the array, which
@ -1209,7 +1209,7 @@ DiskDump.prototype.readDir = function(sDir, fRoot, done)
*/
obj.addManifestInfo(fileInfo);
}
var errSave = null;
for (iFile = 0; iFile < aFiles.length; iFile++) {
if (!aFiles[iFile].FILE_PATH) continue;
@ -1332,7 +1332,7 @@ DiskDump.prototype.readPath = function(sPath, done)
var obj = this;
var cCallbacks = 0;
var errSave = null;
for (iFile = 0; iFile < aFiles.length; iFile++) {
if (!aFiles[iFile].FILE_PATH) continue;
(function readPathEntry(fileInfo) {
@ -1341,7 +1341,7 @@ DiskDump.prototype.readPath = function(sPath, done)
/*
* TODO: See if we can eliminate some of the unfortunate redundancy between the code
* below and the very similar code in readDir(), such as the "README.md" pre-processing.
*
*
* However, in this case, because we want readPath() to support both local and remote
* paths, we call DiskDump.readFile() instead of fs.readFile().
*/
@ -1897,15 +1897,15 @@ DiskDump.prototype.buildImageFromFiles = function(aFiles, done)
*/
var cbMax = (this.mbHD? this.mbHD * 1024 * 1024 : 1440 * 1024);
var cbTotal = this.calcFileSizes(aFiles);
if (fDebug) console.log("total calculated size for " + aFiles.length + " files/folders: " + cbTotal + " bytes (0x" + str.toHex(cbTotal) + ")");
if (cbTotal >= cbMax) {
err = new Error("file(s) too large (" + cbTotal + " bytes total, " + cbMax + " bytes maximum)");
done(err);
return false;
}
var abBoot, cbSector, cSectorsPerCluster, cbCluster, cFATs, cFATSectors;
var cRootEntries, cRootSectors, cTotalSectors, cSectorsPerTrack, cHeads, cDataSectors, cbAvail;
@ -2027,7 +2027,7 @@ DiskDump.prototype.buildImageFromFiles = function(aFiles, done)
offDisk += cClusters * cSectorsPerCluster * cbSector;
if (fDebug) console.log(offDisk + " bytes written, " + cbDisk + " bytes available");
if (offDisk > cbDisk) {
err = new Error("too much data for disk image (" + cClusters + " clusters required)");
done(err);
@ -2064,17 +2064,17 @@ DiskDump.prototype.convertToJSON = function()
try {
var cbDiskData = this.bufDisk.length;
// console.log("length of buffer: " + cbDiskData);
var nHeads = 0;
var nCylinders = 0;
var nSectorsPerTrack = 0;
var aTracks = []; // track array (used only for disk images with track tables)
var iTrack, cbTrack, offTrack, bufTrack, bufSector;
var cbSector = 512; // default sector size
var offBootSector = 0;
if (cbDiskData >= 3000000) { // arbitrary threshold between diskette image sizes and hard disk image sizes
/*
* In this case, the first sector should be an MBR; find the active partition entry,
@ -2093,10 +2093,10 @@ DiskDump.prototype.convertToJSON = function()
* other reserved sectors.
*/
}
var bByte0 = this.bufDisk.readUInt8(offBootSector + DiskDump.BPB.JMP_OPCODE);
var cbSectorBPB = this.bufDisk.readUInt16LE(offBootSector + DiskDump.BPB.SECTOR_BYTES);
/*
* These checks are not only necessary for DOS 1.x diskette images (and other pre-BPB images),
* but also non-DOS diskette images (eg, CPM-86 diskettes).
@ -2134,7 +2134,7 @@ DiskDump.prototype.convertToJSON = function()
}
}
}
if (!nHeads) {
/*
* Next, check for a DSK header (an old private format I used to use, which begins with either 0x00 (read-write) or 0x01 (write-protected),
@ -2180,7 +2180,7 @@ DiskDump.prototype.convertToJSON = function()
}
}
}
if (nHeads) {
/*
* Output the disk data as an array of cylinders, each containing an array of tracks (one track per head),
@ -2194,9 +2194,9 @@ DiskDump.prototype.convertToJSON = function()
json = this.dumpLine(2, "[", "DiskDump of " + this.sDiskPath + " via " + DiskDump.sNotice);
}
for (var iCylinder=0; iCylinder < nCylinders; iCylinder++) {
// if (fDebug) console.log("dumping cylinder " + iCylinder);
var aHeads;
if (this.fJSONNative) {
aHeads = new Array(nHeads);
@ -2206,9 +2206,9 @@ DiskDump.prototype.convertToJSON = function()
}
var offHead = 0;
for (var iHead=0; iHead < nHeads; iHead++) {
// if (fDebug) console.log(" dumping head " + iHead);
if (aTracks.length) {
var aTrack = aTracks[iTrack++];
nSectorsPerTrack = aTrack[0];
@ -2218,9 +2218,9 @@ DiskDump.prototype.convertToJSON = function()
} else {
bufTrack = this.bufDisk.slice(offTrack + offHead, offTrack + offHead + cbTrack);
}
// if (fDebug) console.log(" track buffer length: " + bufTrack.length);
var aSectors;
if (this.fJSONNative) {
aSectors = new Array(nSectorsPerTrack);
@ -2228,13 +2228,13 @@ DiskDump.prototype.convertToJSON = function()
} else {
json += this.dumpLine(2, "[", "head:" + this.sJSONWhitespace + iHead + ", track:" + this.sJSONWhitespace + iCylinder);
}
for (var iSector=1, offSector=0; offSector < cbTrack; iSector++, offSector += cbSector) {
var sector = {};
// if (fDebug) console.log(" dumping sector " + iSector);
bufSector = bufTrack.slice(offSector, offSector + cbSector);
if (this.fJSONNative) {
sector['sector'] = iSector;
@ -2304,7 +2304,7 @@ DiskDump.prototype.convertToJSON = function()
/**
* convertOSIDiskToJSON()
*
*
* This is called when we detect a "CW" signature at offset 0x900 of bufDisk, so we'll try parsing the data
* as an OSI disk image, and output the data in JSON as an array of heads, each containing an array of tracks,
* like so:
@ -2344,7 +2344,7 @@ DiskDump.prototype.convertOSIDiskToJSON = function()
var iTrack = 0;
var offTrack = 0;
var cbTrack = 0x900; // this is the raw track length for a 40-track 5.25-inch disk image
if (this.fJSONNative) {
json = "";
} else {
@ -2352,7 +2352,7 @@ DiskDump.prototype.convertOSIDiskToJSON = function()
}
json += this.dumpLine(2, "[");
json += this.dumpLine(2, "["); // begin array of heads
while (true) {
var bufSector;
var bufTrack = this.bufDisk.slice(offTrack, offTrack + cbTrack);
@ -2449,7 +2449,7 @@ DiskDump.prototype.convertOSIDiskToJSON = function()
* convertToIMG()
*
* Converts the disk image data to a Buffer.
*
*
* TODO: Consider creating a caching mechanism for these requests (ie, stash a limited number of these
* disk images under /tmp, using a name based on a hash of the source path).
*
@ -2507,7 +2507,7 @@ DiskDump.prototype.convertToIMG = function()
* sectors per track, etc).
*/
var buf = null;
try {
/*
* We need to be prepared for any number of errors due to malformed data; in fact, it's entirely
@ -2518,10 +2518,10 @@ DiskDump.prototype.convertToIMG = function()
var nHeads = this.dataDisk[0].length;
var nSectorsPerTrack = this.dataDisk[0][0].length;
var cbDisk = nCylinders * nHeads * nSectorsPerTrack * 512;
var off = 0;
buf = new Buffer(cbDisk);
/*
* WARNING: Buffers are NOT zero-initialized, so we need explicitly fill it with zeros (this seems to
* be a reversal in the trend to zero buffers, when security concerns used to trump performance concerns).
@ -2596,7 +2596,7 @@ DiskDump.prototype.convertToIMG = function()
}
if (buf.length < 3000000) { // arbitrary threshold between diskette image sizes and hard disk image sizes
/*
* Mimic the BPB test in convertToJSON(), because we don't want to blast an OEM string into non-DOS diskette images
* Mimic the BPB test in convertToJSON(), because we don't want to blast an OEM string into non-DOS diskette images
*/
var bByte0 = buf.readUInt8(DiskDump.BPB.JMP_OPCODE);
var cbSectorBPB = buf.readUInt16LE(DiskDump.BPB.SECTOR_BYTES);
@ -2611,7 +2611,7 @@ DiskDump.prototype.convertToIMG = function()
DiskDump.logError(err);
return null;
}
this.bufDisk = buf;
}
return this.bufDisk;

View file

@ -32,12 +32,12 @@
"use strict";
var fs = require("fs");
var path = require("path");
var mkdirp = require("mkdirp");
var net = require("../../shared/lib/netlib");
var proc = require("../../shared/lib/proclib");
var str = require("../../shared/lib/strlib");
var fs = require("fs");
var path = require("path");
var mkdirp = require("mkdirp");
var net = require("../../shared/lib/netlib");
var proc = require("../../shared/lib/proclib");
var str = require("../../shared/lib/strlib");
var DumpAPI = require("../../shared/lib/dumpapi");
/**
@ -661,7 +661,7 @@ FileDump.prototype.outputFile = function(sOutputFile, fOverwrite)
} else {
/*
* We'll dump JSON to the console, but not a raw file buffer; we could add an option to
* "stringify" buffers, but if that's what the caller wants, they should use "--format=json".
* "stringify" buffers, but if that's what the caller wants, they should use "--format=json".
*/
if (typeof data == "string") {
console.log(data);

View file

@ -41,10 +41,10 @@ var glob = require("glob");
var HTTPAPI = require("./httpapi");
var DumpAPI = require("../../shared/lib/dumpapi");
var MarkOut = require("../../markout");
var net = require("../../shared/lib/netlib");
var proc = require("../../shared/lib/proclib");
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var net = require("../../shared/lib/netlib");
var proc = require("../../shared/lib/proclib");
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
/**
* @class exports

View file

@ -32,24 +32,24 @@
"use strict";
var fs = require("fs");
var path = require("path");
var fs = require("fs");
var path = require("path");
/**
* @class exports
* @property {function(string)} sync
*/
var mkdirp = require("mkdirp");
var mkdirp = require("mkdirp");
var DiskAPI = require("../../shared/lib/diskapi");
var DumpAPI = require("../../shared/lib/dumpapi");
var DiskDump = require("../../diskdump");
var FileDump = require("../../filedump");
var UserAPI = require("../../shared/lib/userapi");
var ReportAPI = require("../../shared/lib/reportapi");
var net = require("../../shared/lib/netlib");
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var DiskAPI = require("../../shared/lib/diskapi");
var DumpAPI = require("../../shared/lib/dumpapi");
var UserAPI = require("../../shared/lib/userapi");
var ReportAPI = require("../../shared/lib/reportapi");
var net = require("../../shared/lib/netlib");
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var DiskDump = require("../../diskdump");
var FileDump = require("../../filedump");
/**
* @type {HTMLOut}
@ -59,7 +59,7 @@ var HTMLOut;
/**
* sServerRoot is the root directory of the web server; it can (and should) be overridden using by the Express
* web server using setRoot().
*
*
* @type {string}
*/
var sServerRoot = "";
@ -70,7 +70,7 @@ var sServerRoot = "";
* the entries on the left-hand side should contain trailing slashes.
*
* FYI, here's what the jsmachines.net .htaccess contained before we retired its Apache webserver:
*
*
* Redirect permanent /c1p /docs/c1pjs/
* Redirect permanent /c1pjs /docs/c1pjs/
* Redirect permanent /pc /docs/pcjs/
@ -118,10 +118,10 @@ var externalRedirects = {
* and continue until a match is found, at which point the okens =cement is performed and comparisons stop;
* we could make the replacement process "additive", by continuing comparisons/replacements until the
* end is reached, but let's not, unless there's an actual need.
*
*
* In fact, until I find a compelling need for any of these redirects, I'm going to disable them, so that we
* don't waste time running RegExp tests on every server request.
*
*
* Feel free to use subgroups on the left-hand side, and references to them (eg, $1, $2, etc) on the right.
*/
var internalRedirects = {
@ -238,7 +238,7 @@ HTTPAPI.filterAPI = function(req, res, next)
/**
* hasAPICommand(req, asCommands)
*
*
* @param {Object} req
* @param {Array.<string>} asCommands (eg, DumpAPI.asDiskCommands or DumpAPI.asFileCommands)
* @returns {Array|null}
@ -256,7 +256,7 @@ HTTPAPI.hasAPICommand = function(req, asCommands)
/**
* initUserVolume(vol, fd, cbInit)
*
*
* @param {Object} vol
* @param {number} fd
* @param {number} cbInit
@ -326,7 +326,7 @@ HTTPAPI.openUserVolume = function(sPath, sMachine, sUser, sMode, cbInit, done)
* and return DiskAPI.FAIL.REVOKED; otherwise, there's no real protection of volume
* integrity here. One of the challenges will be ensuring the list of revoked machine
* IDs doesn't grow without bound.
*
*
* When revoking, we should also be able to reuse the current vol by simply updating its
* machine ID; there's no need to close and re-open the file (although assuming revocation
* is a rare occurrence, it shouldn't much matter).
@ -338,17 +338,17 @@ HTTPAPI.openUserVolume = function(sPath, sMachine, sUser, sMode, cbInit, done)
return;
}
}
if (!vol) {
vol = {fd: null, mode: sMode, path: sPath, machine: sMachine};
userVolumes[sUser] = vol;
}
var nResponse = 200;
var sResponse = null;
if (!vol.fd) {
HTMLOut.logDebug('HTMLOut.openUserVolume("' + sPath + '")');
fs.open(sPath, "r+", function(err, fd) {
@ -406,9 +406,9 @@ HTTPAPI.readUserVolume = function(sPath, fd, aCHS, aAddr, done)
{
var pos = (aAddr[0] * (aCHS[1] * aCHS[2] * aCHS[3])) + (aAddr[1] * (aCHS[2] * aCHS[3])) + ((aAddr[2] - 1) * aCHS[3]);
var len = (aAddr[3] * aCHS[3]);
HTMLOut.logDebug('HTMLOut.readUserVolume("' + sPath + '"): pos: ' + pos + ', len: ' + len);
var buf = new Buffer(len);
fs.read(fd, buf, 0, len, pos, function(err, cbRead, buffer) {
var nResponse = 200;
@ -423,7 +423,7 @@ HTTPAPI.readUserVolume = function(sPath, fd, aCHS, aAddr, done)
/*
* Replace the preceding line with this if you want to test how well the client deals with long I/O delays (eg, 10 seconds)
*
setTimeout(function() {
setTimeout(function() {
HTMLOut.logDebug("HTTPAPI.readUserVolume(): responding after 10000ms delay");
done(nResponse, sResponse);
}, 10000);
@ -519,7 +519,7 @@ HTTPAPI.closeUserVolume = function(sPath, sMachine, sUser, done)
/**
* parseDiskValues(s, aDefaults)
*
*
* @param {string} s
* @param {Array.<number>} a
* @returns {Array.<number>}
@ -546,7 +546,7 @@ HTTPAPI.parseDiskValues = function(s, a)
HTTPAPI.processDiskAPI = function(req, res)
{
/*
* For every volume, we must maintain the active machine+user that currently has access.
* For every volume, we must maintain the active machine+user that currently has access.
*/
var nResponse = 400; // default to "Bad Request"
var reqParms = req.method == "GET"? req.query : req.body;
@ -598,7 +598,7 @@ HTTPAPI.processDiskAPI = function(req, res)
* Without the addition of "no-store", Chrome will assume that a previous response to
* a previously seen URL can be re-used without hitting the server again, which would be
* bad if the requested sector(s) had been written in the meantime.
*
*
* Perhaps I should be using different HTTP verbs, or perhaps I should switch to sockets,
* but in the meantime, this is absolutely necessary.
*/
@ -642,7 +642,7 @@ HTTPAPI.processDiskAPI = function(req, res)
});
return true;
}
res.status(nResponse).send(DiskAPI.FAIL.BADVOL);
return true;
};
@ -659,7 +659,7 @@ HTTPAPI.processDumpAPI = function(req, res)
var aCommand;
var nResponse = 400; // default to "Bad Request"
var sDisk, sFile, sFormat, fComments;
if ((aCommand = HTTPAPI.hasAPICommand(req, DumpAPI.asDiskCommands))) {
sDisk = aCommand[1];
@ -669,11 +669,11 @@ HTTPAPI.processDumpAPI = function(req, res)
* Allowing ".." in a path component is risky, unless we're running locally...
*/
if (sDisk.indexOf("..") < 0 || req.app.settings.port == 8088) {
sFormat = req.query[DumpAPI.QUERY.FORMAT] || DumpAPI.FORMAT.JSON;
fComments = (req.query[DumpAPI.QUERY.COMMENTS]? true : false);
var mbHD = req.query[DumpAPI.QUERY.MBHD];
/*
* TODO: Consider adding support for DiskDump's "exclusion" option to the API interface
* (the command-line interface supports it).
@ -692,7 +692,7 @@ HTTPAPI.processDumpAPI = function(req, res)
}
}
else if ((aCommand = HTTPAPI.hasAPICommand(req, DumpAPI.asFileCommands))) {
sFile = aCommand[1];
HTMLOut.logDebug('HTTPAPI.processDumpAPI("' + sFile + '"): type=' + aCommand[0]);
@ -700,7 +700,7 @@ HTTPAPI.processDumpAPI = function(req, res)
* Allowing ".." in a path component is too risky, unless we're running locally.
*/
if (sFile.indexOf("..") < 0 || req.app.settings.port == 8088) {
sFormat = req.query[DumpAPI.QUERY.FORMAT] || DumpAPI.FORMAT.JSON;
fComments = (req.query[DumpAPI.QUERY.COMMENTS]? true : false);
var fDecimal;
@ -730,7 +730,7 @@ HTTPAPI.processDumpAPI = function(req, res)
/**
* dumpDisk(err, disk, res)
*
*
* @param {Error} err
* @param {DiskDump} disk
* @param {Object} res
@ -794,7 +794,7 @@ HTTPAPI.processReportAPI = function(req, res)
var sUser = req.body[ReportAPI.QUERY.USER];
var sType = req.body[ReportAPI.QUERY.TYPE];
var sData = req.body[ReportAPI.QUERY.DATA];
HTMLOut.logDebug('HTTPAPI.processReportAPI("' + sApp + '"): ver=' + sVer + ', url=' + sURL + ', type=' + sType);
if (sApp && sVer && sType == ReportAPI.TYPE.BUG && sData) {
@ -879,7 +879,7 @@ HTTPAPI.createUserID = function(sUser, res)
*/
if (iVerified == 1 || iVerified <= 0 && asUsers[0] == net.GORT_COMMAND) {
HTTPAPI.verifyUserID(asUsers[1], res, function doneVerifyUserID(iVerified, resultSecond, res) {
HTMLOut.logDebug('HTTPAPI.doneVerifyUserID("' + asUsers[1] + '"): ' + iVerified);
if (iVerified <= 0) {
@ -906,15 +906,15 @@ HTTPAPI.createUserID = function(sUser, res)
/**
* verifyUserID(sUser, res, done)
*
*
* If a done() handler is specified, the first parameter it receives is iVerified, which
* will be -1 if the "users.log" file hasn't been initialized yet, 0 if the key doesn't exist,
* or a positive number representing the line number at which the key appears.
*
*
* Moreover, when a done() handler is provided, it simply passes the response object (res) to
* done(), which must actually send the response, based on the provided result; this function sends
* a response only if done() is NOT provided.
*
*
* @param {string} sUser
* @param {Object} res is an Express response object (http://expressjs.com/api.html#res.status)
* @param {function(number, Object, Object)} [done]
@ -923,7 +923,7 @@ HTTPAPI.createUserID = function(sUser, res)
HTTPAPI.verifyUserID = function(sUser, res, done)
{
HTMLOut.logDebug('HTTPAPI.verifyUserID("' + sUser + '")');
/*
* If a colon separator is present, this is an implicit REQ.CREATE call
* (in fact, at present, PCjs does not issue any explicit REQ.CREATE calls).
@ -934,7 +934,7 @@ HTTPAPI.verifyUserID = function(sUser, res, done)
var iVerified = -1;
var sUserFile = path.join(sServerRoot, "/logs/users.log");
fs.readFile(sUserFile, {encoding: "utf8"}, function doneReadUserIDs(err, sData) {
var sResCode = UserAPI.CODE.FAIL;
var sResData = UserAPI.FAIL.VERIFY;
if (err) {
@ -975,8 +975,8 @@ HTTPAPI.getUserDir = function(sUser)
/**
* createUserDir(sUser)
*
* TODO: Creation is relatively rare, so I'm lazy and use synchronous calls, but fix this someday.
*
* TODO: Creation is relatively rare, so I'm lazy and use synchronous calls, but fix this someday.
*
* @param {string} sUser
* @return {boolean} true if successful, false if not
@ -1054,7 +1054,7 @@ HTTPAPI.loadUserData = function(sUser, sState, res)
* Without the addition of "no-store", Chrome (and perhaps other browsers) will assume that
* a previous response to a previously seen URL can be re-used without hitting the server
* again, which would be bad if the requested state has been modified in the meantime.
*
*
* Here's the scenario: the user loads a web page with a machine that uses server-side state,
* the user changes the state of that machine and switches away from the machine (eg, clicks
* a link to a different page), which causes the state to be updated on the server; then they

View file

@ -35,30 +35,30 @@
*
* TODO: Consider adding support for GFM-style tables, as described [here](https://help.github.com/articles/github-flavored-markdown#tables);
* this would be nice for a Markdown-based ASCII table, for example.
*
*
* TODO: Consider adding support for GFM-style strike-through, as described [here](https://help.github.com/articles/github-flavored-markdown#strikethrough)
*
*
* TODO: Consider adding support for anything in the Markdown spec that we don't currently support (but only features that I might actually want to use).
*/
"use strict";
var path = require("path");
var net = require("../../shared/lib/netlib");
var proc = require("../../shared/lib/proclib");
var str = require("../../shared/lib/strlib");
var path = require("path");
var net = require("../../shared/lib/netlib");
var proc = require("../../shared/lib/proclib");
var str = require("../../shared/lib/strlib");
/**
* @class exports
* @property {string} name
* @property {string} version
*/
var pkg = require("../../../package.json");
var pkg = require("../../../package.json");
/**
* fConsole controls diagnostic messages; it is false by default and can be overridden using the
* setOptions() 'console' property.
*
*
* @type {boolean}
*/
var fConsole = false;
@ -123,7 +123,7 @@ function MarkOut(sMD, sIndent, req, aParms, fDebug)
/**
* CLI()
*
*
* Provides a command-line interface for the markout module
*
* Usage:
@ -147,7 +147,7 @@ MarkOut.CLI = function()
{
var fs = require("fs");
var path = require("path");
var fDebug = false;
var args = proc.getArgs();
@ -261,7 +261,7 @@ MarkOut.prototype.getMachines = function()
/**
* generateID(sText)
*
*
* Generate an ID from the given text, by basically converting it to lower case, converting anything
* that's not a letter or a digit to a hyphen (-), and stripping all leading and trailing hyphens from
* the result. Furthermore, if the generated ID is not unique (among the set of ALL generated IDs),
@ -315,7 +315,7 @@ MarkOut.prototype.convertMD = function(sIndent)
* to replace any \r\n sequences with \n.
*/
sMD = str.replaceArray(MarkOut.aHTMLEntities, sMD).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
/*
* Before performing the original comment-elimination step, a new step has been added that
* allows blocks of Markdown to be excluded from the Markout process (eg, build instructions
@ -399,7 +399,7 @@ MarkOut.prototype.convertMD = function(sIndent)
* Ready to convert all blocks now.
*/
sMD = this.convertMDBlocks(sMD, sIndent);
/*
* Post-processing hacks go here. First off, we would like all <pre>...</pre><pre>...</pre> sequences
* to become one single (unified) <pre> sequence.
@ -435,13 +435,13 @@ MarkOut.prototype.convertMDBlocks = function(sMD, sIndent)
sMD = str.replaceArray({"<h######":"<h6", "<h#####":"<h5", "<h####":"<h4", "<h###":"<h3", "<h##":"<h2", "<h#":"<h1"}, sMD);
sMD = str.replaceArray({"h######>":"h6>", "h#####>":"h5>", "h####>":"h4>", "h###>":"h3>", "h##>":"h2>", "h#>":"h1>"}, sMD);
/*
* Convert all "Setext-style headers" (ie, series of equal-signs or dashes) to their <h#> equivalents.
*/
sMD = sMD.replace(/([^\n]+)\n([=-])[=-]*(\n|$)/g, "<h$2>$1</h$2>\n\n");
sMD = str.replaceArray({"h=>":"h1>", "h->":"h2>"}, sMD);
/*
* Auto-generate IDs for headings
*/
@ -710,20 +710,20 @@ MarkOut.prototype.convertMDLines = function(s)
* we strip the '#' and use the remainder of the link as the name of the anchor. To reference a named
* anchor from another link, you have to specify a path with '#' and the anchor name appended, in order
* to distinguish an anchor name from an anchor reference.
*
*
* Note that the need for named anchors is somewhat diminished now that I automatically generate IDs for
* all heading tags (eg, <h1>); refer to the generateID() function that's used in convertMDBlocks().
*
*
* Another extension to Markdown that I've added is detecting empty parentheses alongside a likely URL,
* and automatically converting it to a link; eg:
*
*
* [http://www.ascii-code.com/]()
*
*
* Also, if a URL contains any asterisks, we replace them with the current version number from "package.json".
*
* I prefer this solution over GFM's "autolinking" solution, which is too "loosey-goosey" for my taste
* (see https://help.github.com/articles/github-flavored-markdown#url-autolinking).
*
*
* TODO: Consider adding support for "reference"-style Markdown links.
*
* @this {MarkOut}
@ -794,11 +794,11 @@ MarkOut.prototype.convertMDImageLinks = function(sBlock, sIndent)
* link:url[:width[:height]]
*
* If "link:" is specified, a URL is required, but image width and height are optional.
*
*
* Alternatively:
*
*
* link:url:nogallery[:width[:height]]
*
*
* to disable the automatic "gallery-ification" of image links (an optional width and height
* can still follow).
*/
@ -814,11 +814,11 @@ MarkOut.prototype.convertMDImageLinks = function(sBlock, sIndent)
* If the image link (aMatch[2]) contains "static/" but the sURL is external, AND we're in
* "reveal mode", then transform sURL into a "static/" URL as well; encodeURL() will take care
* of the rest of the transformation.
*
*
* This feature is used with READMEs like /pubs/pc/programming/README.md, where normally we
* want to link to documents stored on sites like archive.org, minuszerodegrees.net or bitsavers,
* unless you're in "reveal mode", in which case we'll serve up our own "backup copies".
*
*
* The assumption here is that if we have "static" thumbs, then we should also have full "static"
* copies as well.
*/
@ -908,7 +908,7 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
* We don't have the XML file open here, and I don't think it's worth the hit to open it.
* Besides, the XML config file isn't necessarily on the same server (although whenever this
* script is being used, it very likely is).
*
*
* TODO: Consider cracking open the XML file anyway, even though the Markdown module is supposed
* to be non-blocking; I'd like to be smarter about defaults (eg, specifying "debugger" when the
* XML file clearly needs it).
@ -926,7 +926,7 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
var sMachineFunc = "embed" + sMachine;
var sMachineClass = sMachine.toLowerCase();
var aMachineParms = aMatch[4].split(':');
var sMachineMessage = "(If you see this message, " + sMachine + "js may still be loading)";
var sMachineMessage = "(If you see this message, " + sMachine + "js is still loading, or JavaScript has been disabled)";
var sMachineID = aMachineParms[0];
var sMachineXSLFile = aMachineParms[1] || "";
@ -957,11 +957,11 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
/*
* Now that we're providing all of the following machine information to addMachine(), we don't
* need to install the machine embed code here; processMachines() in HTMLOut will take care of that now.
* need to install the machine embed code here; processMachines() in HTMLOut will take care of that now.
*
sReplacement += this.sIndent + '<script type="text/javascript">\n' + this.sIndent + 'window.' + sMachineFunc + '("' + sMachineID + '","' + sMachineXMLFile + '","' + sMachineXSLFile + '");\n' + this.sIndent + '</script>';
*/
sBlock = sBlock.replace(aMatch[0], sReplacement);
reMachines.lastIndex = 0; // reset lastIndex, since we just modified the string that reMachines is iterating over
cMatches++;
@ -987,17 +987,17 @@ MarkOut.prototype.convertMDMachineLinks = function(sBlock)
/**
* convertMDEmphasis(sBlock)
*
*
* We look for sequences like **strength**, __strength__, *emphasis* and _emphasis_;
* we convert the stronger (double-character) forms first, followed by the weaker
* (single-character) forms, since we don't want to misconstrue the former as containing
* the latter.
*
*
* Also, standard Markdown says that "if you surround an * or _ with spaces, itll be
* treated as a literal asterisk or underscore." Well, we don't. You can already escape
* special characters with a backslash to make them literal, so I don't feel like
* complicating the RegExps below to accommodate a syntax I don't use or want to support.
*
*
* Also, for reasons noted in the code below, we don't support emphasis in the middle
* of words.
*
@ -1009,17 +1009,17 @@ MarkOut.prototype.convertMDEmphasis = function(sBlock)
{
/*
* Standard Markdown allows * or _ in the middle of a word, as in:
*
*
* un*frigging*believable
*
*
* but we do not. That's because a Markdown link like:
*
*
* [my_modules](/my_modules/)
*
*
* would otherwise be misconstrued as containing emphasis (and it doesn't
* matter whether we process emphasis BEFORE or AFTER links -- an HTML link
* poses the same problem as a Markdown link).
*
*
* To resolve this, I require something non-alphanumeric to both precede AND
* follow the emphasis characters. I would expect that "something" to normally
* be whitespace, but we make it a bit more flexible, so that you can do things
@ -1103,4 +1103,4 @@ MarkOut.prototype.encodeWhitespace = function(sText)
};
*/
module.exports = MarkOut;
module.exports = MarkOut;

View file

@ -34,15 +34,15 @@
"use strict";
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var Component = require("../../shared/lib/component");
var Memory = require("./mem");
var State = require("./state");
var str = require("../../shared/lib/strlib");
var Component = require("../../shared/lib/component");
var Memory = require("./mem");
var State = require("./state");
}
/**
* Bus(cpu, dbg)
*
*
* The Bus component manages "physical" memory and I/O address spaces.
*
* The Bus component has no UI elements, so it does not require an init() handler,
@ -81,7 +81,7 @@ function Bus(parmsBus, cpu, dbg)
/*
* Compute all the Bus memory block addressing values that we rely on, based on the width of the bus.
*
*
* Regarding this.blockTotal, we want to avoid address-overflow-detection expressions like:
*
* iBlock < this.blockTotal? iBlock : 0
@ -106,12 +106,12 @@ function Bus(parmsBus, cpu, dbg)
* this.blockLimit Bus.BLOCK.LIMIT 0xfff
* this.blockTotal Bus.BLOCK.TOTAL ((this.addrLimit + this.blockSize) / this.blockSize) | 0
* this.blockMask Bus.BLOCK.MASK (this.blockTotal - 1) (ie, 0xff)
*
*
* Note that the blockShift calculation below chooses a 4Kb physical memory block size for a 20-bit bus
* (1Mb address space) and a 16Kb physical memory block for a 24-bit bus (16Mb address space). This yields
* a 256-block array for the smaller bus and a 1024-block array for the larger bus. If we left the block
* size at 4Kb in all cases, we'd end up with a 4096-block array for an 80286, which seems a bit excessive.
*
*
* I can't think of any reason why a coarser block granularity (of 16Kb) should hurt anything, other than
* wasting a little memory for ROMs smaller than the block size. Realize that this is strictly a physical
* memory implementation detail, which should have no bearing on segment or page granularity of any future
@ -138,11 +138,11 @@ function Bus(parmsBus, cpu, dbg)
* WARNING: Unlike the (old) read and write memory notification functions, these support only one
* pair of input/output functions per port. A more sophisticated architecture could support a list
* of chained functions across multiple components, but I doubt that will be necessary here.
*
*
* UPDATE: The Debugger now piggy-backs on these arrays to indicate ports for which it wants notification
* of I/O. In those cases, the registered component/function elements may or may not be set, but the following
* additional element will be set:
*
*
* [2]: true to break on I/O, false to ignore I/O
*
* The false case is important if fPortInputBreakAll and/or fPortOutputBreakAll is set, because it allows the
@ -163,7 +163,7 @@ Component.subclass(Component, Bus);
/**
* initMemory()
*
*
* Allocate enough (empty) Memory blocks to span the entire physical address space.
*
* @this {Bus}
@ -192,7 +192,7 @@ Bus.prototype.reset = function()
/**
* addMemory(addr, size, fReadOnly, controller)
*
*
* Adds new Memory blocks to the specified address range. Any Memory blocks previously
* added to that range must first be removed via removeMemory(); otherwise, you'll get
* an allocation conflict error. Moreover, the address range must start at a block-granular
@ -228,7 +228,7 @@ Bus.prototype.addMemory = function(addr, size, fReadOnly, controller)
/**
* cleanMemory(addr, size)
*
*
* @this {Bus}
* @param {number} addr
* @param {number} size
@ -318,7 +318,7 @@ Bus.prototype.setMemoryAccess = function(addr, size, afn)
* removeMemory(addr, size)
*
* Replaces every block in the specified address range with empty Memory blocks that will ignore all reads/writes.
*
*
* @this {Bus}
* @param {number} addr
* @param {number} size
@ -410,7 +410,7 @@ Bus.prototype.setWordDirect = function(addr, w)
* All dirty blocks will be stored in a single array, as pairs of block numbers and data arrays, like so:
*
* [iBlock0, [dw0, dw1, ...], iBlock1, [dw0, dw1, ...], ...]
*
*
* In a normal 4Kb block, there will be 1K DWORD values in the data array. Remember that each DWORD is a signed 32-bit
* integer (because they are formed using bit-wise operator rather than floating-point math operators), so don't be
* surprised to see negative numbers in the data.
@ -459,7 +459,7 @@ Bus.prototype.saveMemory = function()
* component to be restored, all those blocks (and their attributes) should be in place now.
*
* See saveMemory() for a description of how the memory block contents are saved.
*
*
* @this {Bus}
* @param {Array} a
* @return {boolean} true if successful, false if not
@ -490,7 +490,7 @@ Bus.prototype.restoreMemory = function(a)
/**
* addMemoryBreakpoint(addr, fWrite)
*
*
* @this {Bus}
* @param {number} addr
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
@ -505,7 +505,7 @@ Bus.prototype.addMemoryBreakpoint = function(addr, fWrite)
/**
* removeMemoryBreakpoint(addr, fWrite)
*
*
* @this {Bus}
* @param {number} addr
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
@ -540,7 +540,7 @@ Bus.prototype.addPortInputBreak = function(port)
/**
* addPortInputNotify(start, end, component, fn)
*
*
* Add a port input-notification handler to the list of such handlers.
*
* @this {Bus}
@ -565,7 +565,7 @@ Bus.prototype.addPortInputNotify = function(start, end, component, fn)
/**
* addPortInputTable(component, table, offset)
*
*
* Add port input-notification handlers from the specified table (a batch version of addPortInputNotify)
*
* @this {Bus}
@ -578,7 +578,7 @@ Bus.prototype.addPortInputTable = function(component, table, offset)
if (offset === undefined) offset = 0;
for (var port in table) {
/*
* JavaScript coerces property keys to strings, so we use parseInt() to coerce them back to numbers.
* JavaScript coerces property keys to strings, so we use parseInt() to coerce them back to numbers.
*/
port = parseInt(port, 10);
this.addPortInputNotify(port + offset, port + offset, component, table[port]);
@ -587,7 +587,7 @@ Bus.prototype.addPortInputTable = function(component, table, offset)
/**
* checkPortInputNotify(port, addrFrom)
*
*
* @this {Bus}
* @param {number} port
* @param {number} [addrFrom] is the EIP value at the time of the input
@ -619,7 +619,7 @@ Bus.prototype.checkPortInputNotify = function(port, addrFrom)
/**
* removePortInputNotify(start, end, component, fn)
*
*
* Remove a port input-notification handler from the list of such handlers (to be ENABLED later if needed)
*
* @this {Bus}
@ -660,7 +660,7 @@ Bus.prototype.addPortOutputBreak = function(port)
/**
* addPortOutputNotify(start, end, component, fn)
*
*
* Add a port output-notification handler to the list of such handlers.
*
* @this {Bus}
@ -685,7 +685,7 @@ Bus.prototype.addPortOutputNotify = function(start, end, component, fn)
/**
* addPortOutputTable(component, table, offset)
*
*
* Add port output-notification handlers from the specified table (a batch version of addPortOutputNotify)
*
* @this {Bus}
@ -698,7 +698,7 @@ Bus.prototype.addPortOutputTable = function(component, table, offset)
if (offset === undefined) offset = 0;
for (var port in table) {
/*
* JavaScript converts property keys to strings (brilliant), so we use parseInt() to convert them back to numbers.
* JavaScript converts property keys to strings (brilliant), so we use parseInt() to convert them back to numbers.
*/
port = parseInt(port, 10);
this.addPortOutputNotify(port + offset, port + offset, component, table[port]);
@ -707,7 +707,7 @@ Bus.prototype.addPortOutputTable = function(component, table, offset)
/**
* checkPortOutputNotify(port, bOut, addrFrom)
*
*
* @this {Bus}
* @param {number} port
* @param {number} bOut
@ -734,7 +734,7 @@ Bus.prototype.checkPortOutputNotify = function(port, bOut, addrFrom)
/**
* removePortOutputNotify(start, end, component, fn)
*
*
* Remove a port output-notification handler from the list of such handlers (to be ENABLED later if needed)
*
* @this {Bus}

File diff suppressed because it is too large Load diff

View file

@ -53,11 +53,11 @@
* The PCjs JavaScript files do have some initialization-order dependencies.
* If you load the files individually, it's recommended that you load them in
* the same order that they're compiled.
*
*
* Generally speaking, component.js should be first, computer.js should be
* last (of the files based on component.js), and panel.js should be listed
* early so that the Control Panel is ready as soon as possible.
*
*
* Another recent ordering requirement is that rom.js must be loaded before
* ram.js; this was true before, but now it's required, because I'm starting
* to add ROM BIOS Data Area definitions to rom.js, and since the data area
@ -67,19 +67,19 @@
"use strict";
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var web = require("../../shared/lib/weblib");
var UserAPI = require("../../shared/lib/userapi");
var ReportAPI = require("../../shared/lib/reportapi");
var Component = require("../../shared/lib/component");
var Bus = require("./bus");
var State = require("./state");
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var web = require("../../shared/lib/weblib");
var UserAPI = require("../../shared/lib/userapi");
var ReportAPI = require("../../shared/lib/reportapi");
var Component = require("../../shared/lib/component");
var Bus = require("./bus");
var State = require("./state");
}
/**
* Computer(parmsComputer, parmsMachine, fSuspended)
*
*
* @constructor
* @extends Component
* @param {Object} parmsComputer
@ -93,7 +93,7 @@ if (typeof module !== 'undefined') {
* 20 is the minimum (and the default), which implies 8086/8088 real-mode addressing,
* while 24 is required for 80286 protected-mode addressing. This value is passed
* directly through to the Bus component; see that component for more details.
*
*
* resume: one of the Computer.RESUME constants, which are as follows:
* '0' if resume disabled (default)
* '1' if enabled without prompting
@ -102,7 +102,7 @@ if (typeof module !== 'undefined') {
* or a string containing the path of a predefined JSON-encoded state
*
* state: the path to JSON-encoded state file (see details regarding 'state' below)
*
*
* If a predefined state is supplied AND it's successfully loaded, then resume behavior
* defaults to '1' (ie, resume enabled without prompting).
*
@ -154,12 +154,12 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
this.bus = new Bus({'id': this.idMachine + '.bus', 'buswidth': this.nBusWidth}, this.cpu, this.dbg);
/*
* Iterate through all the components and connect them to the Control Panel, if any
* Iterate through all the components and connect them to the Control Panel, if any
*/
var iComponent, component;
var aComponents = Component.getComponents(this.id);
this.panel = Component.getComponentByType("Panel", this.id);
if (this.panel && this.panel.controlPrint) {
for (iComponent = 0; iComponent < aComponents.length; iComponent++) {
component = aComponents[iComponent];
@ -198,7 +198,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
* The Computer 'state' property allows a state file to be specified independent of the 'resume' feature;
* previously, you could only use 'resume' to load a state file -- which we still support, but loading a state
* file that way prevents the machine's state from being saved, since we always resume from the 'resume' file.
*
*
* The other wrinkle is on the restore side: we need to IGNORE the 'state' property if a saved state now exists.
* So we have to peek at localStorage, and unfortunately, the only way to "peek" is to actually load the data,
* but we're not ready to use it yet, so powerUp() has been changed to use any existing stateComputer that we've
@ -210,7 +210,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
*/
var fAllowResume;
var sState = Component.parmsURL && Component.parmsURL['state'] || (fAllowResume = true) && parmsComputer['state'];
if (sState) {
sStatePath = this.sStatePath = sState;
if (!fAllowResume) {
@ -229,7 +229,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
/*
* If sStatePath is set, we must use it. But if there's no sStatePath AND resume is set,
* then we have the option of resuming from a server-side state, assuming a valid USERID.
* then we have the option of resuming from a server-side state, assuming a valid USERID.
*/
if (!sStatePath && this.resume) {
sStatePath = this.getServerStatePath();
@ -241,7 +241,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
} else {
web.loadResource(sStatePath, true, null, this, this.onLoadSetReady);
}
if (!fSuspended) {
/*
* Power "up" the computer, giving every component the opportunity to reset or restore itself.
@ -317,7 +317,7 @@ Computer.prototype.getUserID = function()
/**
* onLoadSetReady(sStateFile, sStateData, nErrorCode)
*
*
* @this {Computer}
* @param {string} sStateFile
* @param {string} sStateData
@ -327,7 +327,7 @@ Computer.prototype.onLoadSetReady = function(sStateFile, sStateData, nErrorCode)
{
if (!nErrorCode) {
this.sStateData = sStateData;
if (DEBUG) this.messageDebugger("loaded state file " + sStateFile.replace(this.sUserID || "xxx", "xxx"));
if (DEBUG) this.messageDebugger("loaded state file " + sStateFile.replace(this.sUserID || "xxx", "xxx"));
} else {
this.sResumePath = null;
this.fServerState = false;
@ -338,7 +338,7 @@ Computer.prototype.onLoadSetReady = function(sStateFile, sStateData, nErrorCode)
/**
* wait(fn, parms)
*
*
* wait() waits until every component is ready (including ourselves, the last component we check),
* then calls the specified Computer method.
*
@ -376,7 +376,7 @@ Computer.prototype.wait = function(fn, parms)
* validateState(stateComputer)
*
* NOTE: We clear() stateValidate only when there's no stateComputer.
*
*
* @this {Computer}
* @param {State|null} [stateComputer]
* @return {boolean} true if state passes validation, false if not
@ -403,7 +403,7 @@ Computer.prototype.validateState = function(stateComputer)
* powerOn(resume)
*
* Power every component "up", applying any previously available state information.
*
*
* @this {Computer}
* @param {number} [resume] is a valid RESUME value; default is this.resume
*/
@ -455,7 +455,7 @@ Computer.prototype.powerOn = function(resume)
stateComputer.load(sData);
} else {
/*
* A missing (or not yet created) state file is no cause for alarm, but other errors might be
* A missing (or not yet created) state file is no cause for alarm, but other errors might be
*/
if (sCode == UserAPI.CODE.FAIL && sData != UserAPI.FAIL.NOSTATE) {
this.notice("Error: " + sData);
@ -529,7 +529,7 @@ Computer.prototype.powerOn = function(resume)
/**
* powerRestore(component, stateComputer, fRepower, fRestore)
*
*
* @this {Computer}
* @param {Component} component
* @param {State} stateComputer
@ -550,7 +550,7 @@ Computer.prototype.powerRestore = function(component, stateComputer, fRepower, f
* This is a hack that makes it possible for a machine whose ID has been
* supplemented with a suffix (a single letter or digit) to find object IDs
* in states created from a machine without the suffix.
*
*
* For example, if a state file was created from a machine with ID "ibm5160"
* but the current machine is "ibm5160a", this attempts a second lookup with
* "ibm5160", enabling us to find objects that match the original machine ID
@ -565,7 +565,7 @@ Computer.prototype.powerRestore = function(component, stateComputer, fRepower, f
* string comes back, something went wrong. By explicitly eliminating "string" data,
* the Closure Compiler stops complaining that we might be passing strings to our
* powerUp() functions (even though we know we're not).
*
*
* TODO: Determine if there's some way to coerce the Closure Compiler into treating
* data as Object or null, without having to include this runtime check. An assert
* would be a good idea, but this is overkill.
@ -629,7 +629,7 @@ Computer.prototype.powerRestore = function(component, stateComputer, fRepower, f
* powerFinish(aParms)
*
* This is nothing more than a continuation of powerOn(), giving us the option of calling wait() one more time.
*
*
* @this {Computer}
* @param {Array} aParms containing [stateComputer, resume, fRestore]
*/
@ -676,7 +676,7 @@ Computer.prototype.powerFinish = function(aParms)
/**
* powerReport(stateComputer)
*
*
* @this {Computer}
* @param {State} stateComputer
*/
@ -712,7 +712,7 @@ Computer.prototype.powerReport = function(stateComputer)
*
* As it stands, the worst that happens is any manually mounted disk images might have to be manually remounted,
* which doesn't seem like a huge problem.
*
*
* @this {Computer}
* @param {boolean} fSave
* @param {boolean} [fShutdown] is true if the machine is being shut down
@ -809,9 +809,9 @@ Computer.prototype.powerOff = function(fSave, fShutdown)
sState = stateComputer.toString();
}
}
if (fShutdown) this.fPowered = false;
return sState;
};
@ -819,11 +819,11 @@ Computer.prototype.powerOff = function(fSave, fShutdown)
* reset()
*
* Notify all (other) components with a reset() method that the Computer is being reset.
*
*
* NOTE: We'd like to reset the Bus first (due to the importance of the A20 line), but since we
* allocated the Bus object ourselves, after all the other components were allocated, it ends
* up near the end of Component's list of components. Hence the special case for this.bus below.
*
*
* @this {Computer}
*/
Computer.prototype.reset = function()
@ -846,10 +846,10 @@ Computer.prototype.reset = function()
* start(ms, nCycles)
*
* Notify all (other) components with a start() method that the CPU has started.
*
*
* Note that we're called by runCPU(), which is why we exclude the CPU component,
* as well as ourselves.
*
*
* @this {Computer}
* @param {number} ms
* @param {number} nCycles
@ -892,7 +892,7 @@ Computer.prototype.stop = function(ms, nCycles)
/**
* setBinding(sHTMLClass, sHTMLType, sBinding, control)
*
*
* @this {Computer}
* @param {string|null} sHTMLClass is the class of the HTML control (eg, "input", "output")
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
@ -942,7 +942,7 @@ Computer.prototype.resetUserID = function()
/**
* queryUserID(fPrompt)
*
*
* @param {boolean} [fPrompt]
* @returns {string|null|undefined}
*/
@ -968,7 +968,7 @@ Computer.prototype.queryUserID = function(fPrompt)
/**
* verifyUserID(sUserID)
*
*
* @this {Computer}
* @param {string} sUserID
* @return {string} validated user ID, or null if error
@ -1002,7 +1002,7 @@ Computer.prototype.verifyUserID = function(sUserID)
/**
* getServerStatePath()
*
*
* @this {Computer}
* @return {string|null} sStatePath (null if no localStorage or no USERID stored in localStorage)
*/
@ -1020,7 +1020,7 @@ Computer.prototype.getServerStatePath = function()
/**
* saveServerState(sUserID, sState)
*
*
* @param {string} sUserID
* @param {string|null} sState
*/
@ -1054,7 +1054,7 @@ Computer.prototype.saveServerState = function(sUserID, sState)
/**
* storeServerState(sUserID, sState, fSync)
*
*
* @this {Computer}
* @param {string} sUserID
* @param {string} sState
@ -1095,7 +1095,7 @@ Computer.prototype.storeServerState = function(sUserID, sState, fSync)
/**
* onReset()
*
*
* @this {Computer}
*/
Computer.prototype.onReset = function()
@ -1117,7 +1117,7 @@ Computer.prototype.onReset = function()
* and rely on all the components to reset themselves to their default state. The components with
* the greatest burden here are FDC and HDC, which must rely on the fReload flag to determine whether
* or not to unload/reload all their original auto-mounted disk images.
*
*
* However, if we started with a predefined state (ie, sStatePath is set), we take this shortcut, because
* we don't (yet) have code in place to gracefully reload the initial state (requires calling loadResource()
* again); alternatively, we could avoid throwing that state away, but it seems better to save the memory.
@ -1139,7 +1139,7 @@ Computer.prototype.onReset = function()
/**
* getComponentByType(sType, componentPrev)
*
*
* @this {Computer}
* @param {string} sType
* @param {Component} [componentPrev] of previously returned component, if any
@ -1188,19 +1188,19 @@ Computer.prototype.messageDebugger = function(sMessage, fForce)
Computer.init = function()
{
var aeMachines = Component.getElementsByClass(window.document, PCJSCLASS + "-machine");
for (var iMachine = 0; iMachine < aeMachines.length; iMachine++) {
var eMachine = aeMachines[iMachine];
var parmsMachine = Component.getComponentParms(eMachine);
var aeComputers = Component.getElementsByClass(eMachine, PCJSCLASS, "computer");
for (var iComputer = 0; iComputer < aeComputers.length; iComputer++) {
var eComputer = aeComputers[iComputer];
var parmsComputer = Component.getComponentParms(eComputer);
/*
* We set fSuspended in the Computer constructor because we want to "power up" the
* computer ourselves, after any/all bindings are in place.
@ -1208,7 +1208,7 @@ Computer.init = function()
var computer = new Computer(parmsComputer, parmsMachine, true);
if (DEBUG) computer.messageDebugger("onInit(" + computer.fPowered + ")");
/*
* For now, all we support are "reset" and "save" buttons. We may eventually add a "power"
* button to manually suspend/resume the machine. An "erase" button was also considered, but
@ -1216,7 +1216,7 @@ Computer.init = function()
* might be redundant now.
*/
Component.bindComponentControls(computer, eComputer, PCJSCLASS);
/*
* Power "up" the computer, giving every component the opportunity to reset or restore itself.
*/
@ -1238,7 +1238,7 @@ Computer.show = function()
if (computer) {
if (DEBUG) computer.messageDebugger("onShow(" + computer.fInitialized + "," + computer.fPowered + ")");
if (computer.fInitialized && !computer.fPowered) {
/**
* Repower the computer, notifying every component to continue running as-is.
@ -1285,7 +1285,7 @@ Computer.exit = function()
if (computer) {
if (DEBUG) computer.messageDebugger("onExit(" + computer.fPowered + ")");
if (computer.fPowered) {
/**
* Power "down" the computer, giving every component an opportunity to save its state,

View file

@ -34,9 +34,9 @@
"use strict";
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var Component = require("../../shared/lib/component");
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var Component = require("../../shared/lib/component");
}
/**
@ -46,9 +46,9 @@ if (typeof module !== 'undefined') {
*
* cycles: the machine's base cycles per second; the X86CPU constructor will
* provide us with a default (based on the CPU model) to use as a fallback
*
*
* multiplier: base cycle multiplier; default is 1
*
*
* autoStart: true to automatically start, false to not, or null (default)
* to make the autoStart decision based on whether or not a Debugger is
* installed (if there's no Debugger AND no "Run" button, then auto-start,
@ -82,7 +82,7 @@ function CPU(parmsCPU, nCyclesDefault)
var nMultiplier = parmsCPU['multiplier'] || 1;
this.nCyclesPerSecond = nCycles;
/*
* nCyclesMultiplier replaces the old "speed" variable (0, 1, 2) and eliminates the need for
* the constants (SPEED_SLOW, SPEED_FAST and SPEED_MAX). The UI simply doubles the multiplier
@ -113,7 +113,7 @@ function CPU(parmsCPU, nCyclesDefault)
* Get checksum parameters, if any. runCPU() behavior is not affected until fChecksum
* is true, which won't happen until resetChecksum() is called with nCyclesChecksumInterval
* ("csInterval") set to a positive value.
*
*
* As above, any of these parameters can also be set with the Debugger's execution options
* command ("x"); for example, "x cs int 5000" will set nCyclesChecksumInterval to 5000
* and call resetChecksum().
@ -126,7 +126,7 @@ function CPU(parmsCPU, nCyclesDefault)
var cpu = this;
this.onRunTimeout = function() { cpu.runCPU(); };
this.setReady();
}
@ -156,7 +156,7 @@ CPU.STATUS_UPDATES_PER_SECOND = 2;
/**
* initBus(cmp, bus, cpu, dbg)
*
*
* @this {CPU}
* @param {Computer} cmp
* @param {Bus} bus
@ -170,7 +170,7 @@ CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
this.cmp = cmp;
/*
* Attach the Video component to the CPU, so that the CPU can periodically update
* the video display via displayVideo(), as cycles permit.
* the video display via displayVideo(), as cycles permit.
*/
var video = cmp.getComponentByType("Video");
if (video) {
@ -184,7 +184,7 @@ CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
/*
* Attach the ChipSet component to the CPU, so that it can obtain the IDT vector number of
* pending hardware interrupts, in response to ChipSet's updateINTR() notifications.
*
*
* We must also call chipset.updateAllTimers() periodically; stepCPU() takes care of that.
*/
this.chipset = cmp.getComponentByType("ChipSet");
@ -195,7 +195,7 @@ CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
* reset()
*
* This is a placeholder for reset (overridden by the X86CPU component).
*
*
* @this {CPU}
*/
CPU.prototype.reset = function()
@ -206,7 +206,7 @@ CPU.prototype.reset = function()
* save()
*
* This is a placeholder for save support (overridden by the X86CPU component).
*
*
* @this {CPU}
* @return {Object|null}
*/
@ -219,7 +219,7 @@ CPU.prototype.save = function()
* restore(data)
*
* This is a placeholder for restore support (overridden by the X86CPU component).
*
*
* @this {CPU}
* @param {Object} data
* @return {boolean} true if restore successful, false if not
@ -258,7 +258,7 @@ CPU.prototype.powerUp = function(data, fRepower)
* knows if there's a "print" control (this.cmp.panel.controlPrint), and if there IS a "print" control
* but no debugger, the machine is probably misconfigured (most likely, the page simply neglected to
* load the Debugger component).
*
*
* However, we don't actually need to check all that; it's always safe use println(), regardless whether
* a Control Panel with a "print" control is present or not.
*/
@ -273,7 +273,7 @@ CPU.prototype.powerUp = function(data, fRepower)
/**
* powerDown(fSave)
*
*
* @this {CPU}
* @param {boolean} fSave
* @return {Object|boolean}
@ -286,7 +286,7 @@ CPU.prototype.powerDown = function(fSave)
/**
* autoStart()
*
*
* @this {CPU}
* @return {boolean} true if started, false if not
*/
@ -335,9 +335,9 @@ CPU.prototype.isRunning = function()
/**
* getChecksum()
*
*
* This will be implemented by the X86CPU component.
*
*
* @this {CPU}
* @return {number} a 32-bit summation of key elements of the current CPU state (used by the CPU checksum code)
*/
@ -352,7 +352,7 @@ CPU.prototype.getChecksum = function()
* If checksum generation is enabled (fChecksum is true), this resets the running 32-bit checksum and the
* cycle counter that will trigger the next displayChecksum(); called by resetCycles(), which is called whenever
* the CPU is reset or restored.
*
*
* @this {CPU}
* @return {boolean} true if checksum generation enabled, false if not
*/
@ -378,7 +378,7 @@ CPU.prototype.resetChecksum = function()
* number of cycles (1), effectively limiting execution to a single instruction, and then we're called with
* the exact number cycles that were actually executed. This should give us instruction-granular checksums
* at precise intervals that are 100% repeatable.
*
*
* @this {CPU}
* @param {number} nCycles
*/
@ -409,11 +409,11 @@ CPU.prototype.updateChecksum = function(nCycles)
/**
* displayChecksum()
*
*
* When checksum generation is enabled (fChecksum is true), this is called to provide a crude log of all
* checksums generated at the specified cycle intervals, as specified by the "csStart" and "csInterval" parmsCPU
* properties).
*
*
* @this {CPU}
*/
CPU.prototype.displayChecksum = function()
@ -473,7 +473,7 @@ CPU.prototype.displayVideo = function()
/**
* setBinding(sHTMLClass, sHTMLType, sBinding, control)
*
*
* @this {CPU}
* @param {string|null} sHTMLClass is the class of the HTML control (eg, "input", "output")
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
@ -539,7 +539,7 @@ CPU.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, control)
* A divisor greater than 1 (the default) does NOT require us to yield more frequently or update the screen
* more frequently; it only means that stepCPU() must be called more frequently, with correspondingly smaller burst
* cycles, because stepCPU() is responsible for updating all the timers ONCE, each time it's called.
*
*
* @this {CPU}
* @param {number} nDivisor
*/
@ -652,12 +652,12 @@ CPU.prototype.getCycles = function(fScaled)
*
* but that speed will fluctuate somewhat: large fluctuations at first, but increasingly smaller
* fluctuations after each burst of instructions that runCPU() executes.
*
*
* Alternatively, we can scale the cycle count by the multiplier, which is good in that the
* multiplier doesn't vary once the user changes it, but a potential downside is that the
* multiplier might be set too high, resulting in a target speed that's higher than the effective
* speed is able to reach.
*
*
* Also, if multipliers were always limited to a power-of-two, then this could be calculated
* with a simple shift. However, only the "setSpeed" UI binding limits it that way; the Debugger
* interface allows any value, as does the CPU "multiplier" parmsCPU property (from the machine's
@ -717,9 +717,9 @@ CPU.prototype.getSpeed = function() {
*/
CPU.prototype.getSpeedCurrent = function() {
/*
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
*/
return ((this.fRunning && this.mhz)? (this.mhz.toFixed(2) + "Mhz") : "Stopped");
return ((this.fRunning && this.mhz)? (this.mhz.toFixed(2) + "Mhz") : "Stopped");
};
/**
@ -730,14 +730,14 @@ CPU.prototype.getSpeedCurrent = function() {
*/
CPU.prototype.getSpeedTarget = function() {
/*
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
*/
return this.mhzTarget.toFixed(2) + "Mhz";
};
/**
* setSpeed(nMultiplier, fOnClick)
*
*
* @this {CPU}
* @param {number} [nMultiplier] is the new proposed multiplier (reverts to 1 if the target was too high)
* @param {boolean} [fOnClick] is true if called from a click handler that might have stolen focus
@ -774,7 +774,7 @@ CPU.prototype.setSpeed = function(nMultiplier, fOnClick)
/**
* calcSpeed(nCycles, msElapsed)
*
*
* @this {CPU}
* @param {number} nCycles
* @param {number} msElapsed
@ -793,7 +793,7 @@ CPU.prototype.calcSpeed = function(nCycles, msElapsed)
/**
* calcStartTime()
*
*
* @this {CPU}
*/
CPU.prototype.calcStartTime = function()
@ -808,22 +808,22 @@ CPU.prototype.calcStartTime = function()
* Try to detect situations where the browser may have throttled us, such as when the user switches
* to a different tab; in those situations, Chrome and Safari may restrict setTimeout() callbacks
* to roughly one per second.
*
*
* Another scenario: the user resizes the browser window. setTimeout() callbacks are not throttled,
* but there can still be enough of a lag between the callbacks that CPU speed will be noticeably
* erratic if we don't compensate for it here.
*
*
* We can detect throttling/lagging by verifying that msEndThisRun (which was set at the end of the
* previous run and includes any requested sleep time) is comparable to the current msStartThisRun;
* if the delta is significant, we compensate by bumping msRunStart forward by that delta.
*
*
* This shouldn't be triggered when the Debugger halts the CPU, because setSpeed() -- which is called
* whenever the CPU starts running again -- zeroes msEndThisRun.
*
*
* This also won't do anything about other internal delays; for example, Debugger message() calls.
* By the time the message() function has called yieldCPU(), the cost of the message has already been
* incurred, so it will be end up being charged against the instruction(s) that triggered them.
*
*
* TODO: Consider calling yieldCPU() sooner from message(), so that it can arrange for the msEndThisRun
* "snapshot" to occur sooner; it's unclear, however, whether that will really improve the CPU's ability
* to hit its target speed, since you would expect any instruction that displays a message to be an
@ -849,14 +849,14 @@ CPU.prototype.calcStartTime = function()
/**
* calcRemainingTime()
*
*
* @this {CPU}
* @return {number}
*/
CPU.prototype.calcRemainingTime = function()
{
this.msEndThisRun = usr.getTime();
var msYield = this.msPerYield;
if (this.nCyclesThisRun) {
/*
@ -906,7 +906,7 @@ CPU.prototype.calcRemainingTime = function()
/*
* Last but not least, update nRecalcCycles, so that when runCPU() starts up again and calls calcStartTime(),
* it'll be ready to decide if calcCycles() should be called again.
* it'll be ready to decide if calcCycles() should be called again.
*/
this.nRecalcCycles += this.nCyclesThisRun;
@ -920,7 +920,7 @@ CPU.prototype.calcRemainingTime = function()
/**
* runCPU(fOnClick)
*
*
* @this {CPU}
* @param {boolean} [fOnClick] is true if called from a click handler that might have stolen focus
*/
@ -953,7 +953,7 @@ CPU.prototype.runCPU = function(fOnClick)
try {
do {
var nCyclesPerBurst = this.fChecksum? 1 : Math.round(this.nCyclesPerBurst / this.nBurstDivisor);
/*
* This is an alternative to ChipSet calling setBurstDivisor(). Unfortunately, this doesn't seem
* to work as well as setBurstDivisor(); for some reason, the smaller bursts that the burst divisor
@ -965,7 +965,7 @@ CPU.prototype.runCPU = function(fOnClick)
* nCyclesPerBurst = nCyclesTimer0;
* }
*/
/*
* nCyclesPerBurst is how many cycles we WANT to run on each iteration of stepCPU(), but it may run
* significantly less (or slightly more, since we can't execute partial instructions).
@ -1015,7 +1015,7 @@ CPU.prototype.runCPU = function(fOnClick)
/**
* setBurstCycles(nCycles)
*
*
* This function is used by the ChipSet component whenever a very low timer count is set,
* in anticipation of the timer requiring an update sooner than the normal nCyclesPerYield
* period in runCPU() would normally provide.
@ -1046,7 +1046,7 @@ CPU.prototype.setBurstCycles = function(nCycles)
*
* This similar to yieldCPU(), but it doesn't need to zero nCyclesNextYield to break out of runCPU();
* it simply needs to clear fRunning (well, "simply" may be oversimplifying a bit....)
*
*
* @this {CPU}
* @param {boolean} [fComplete]
*/
@ -1067,7 +1067,7 @@ CPU.prototype.haltCPU = function(fComplete)
/**
* stepCPU(nMinCycles)
*
*
* This will be implemented by the X86CPU component.
*
* @this {CPU}
@ -1086,7 +1086,7 @@ CPU.prototype.stepCPU = function(nMinCycles)
* stepCPU() -- needed to have more control over when these updates are performed. However, for
* other callers of stepCPU(), such as the Debugger, the combination of stepCPU() + updateCPU()
* provides the old behavior.
*
*
* @this {CPU}
*/
CPU.prototype.updateCPU = function()
@ -1097,7 +1097,7 @@ CPU.prototype.updateCPU = function()
/**
* yieldCPU()
*
*
* Similar to haltCPU() with regard to how it resets various cycle countdown values, but the CPU
* remains in a "running" state.
*

View file

@ -35,15 +35,15 @@
if (DEBUGGER) {
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var Bus = require("./bus");
var State = require("./state");
var CPU = require("./cpu");
var X86 = require("./x86");
var X86Seg = require("./x86seg");
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var Bus = require("./bus");
var State = require("./state");
var CPU = require("./cpu");
var X86 = require("./x86");
var X86Seg = require("./x86seg");
}
}

View file

@ -51,19 +51,19 @@
/*
* Client/Server Disk I/O
*
*
* To support large disks without consuming large amounts of client-side memory, and to push
* client-side disk changes back the server, we need a DiskIO API that can be used in place of
* the DiskDump API.
*
*
* Use of the DiskIO API and any associated disk images must be tightly coupled to per-user
* storage and specific machine configurations, to prevent the disk images from being corrupted
* by inconsistent I/O operations. Our basic User API (userapi.js) already provides some
* per-user storage that we can use to get the design rolling.
*
*
* The DiskIO API must also provide the ability to create new (empty) hard disk images in per-user
* storage and automatically associate them with the machine configurations that requested them.
*
*
* Principles
* ---
* Originally, when the Disk class was given a disk image to load and mount, it would request the
@ -71,84 +71,84 @@
* for larger disks -- let's just say anything stored on the server as an IMG file -- we'd prefer
* to interact with that disk using "On-Demand I/O". Any IMG file on the same server as the PCjs
* application should be a candidate for on-demand access.
*
*
* On-Demand I/O means that nothing is initially transferred from the server. As sectors are
* requested by the PCjs machine, PCjs requests them from the server, and maintains an MRU cache
* of sectors, periodically discarding the least-used clean sectors above a certain memory limit.
* Dirty sectors (ie, those that the PCjs machine has written to) must be periodically sent
* back to the server and then marked as clean, so that they can be discarded like any other
* sector.
*
*
* We also support "local" init-only disk images, which means that dirty sectors are never sent
* back to the server and are instead retained by the client for the lifetime of the app; such
* images are "read-only" as far as the server is concerned, but "read-write" as far as the client
* is concerned. Reloading/restarting an app with an "local" disk will return the disk to its
* initial state.
*
* initial state.
*
* Practice
* ---
* Let's first look at what we *already* do for the HDC component:
*
*
* 1) Creating new (empty) disk images
* 2) Pre-loading pre-built JSON-encoded disk images (converting them to JSON on the fly as needed)
*
*
* An example of #1 is in /configs/pc/machines/5160/cga/256kb/demo/machine.xml:
*
*
* <hdc id="hdcXT" drives='[{name:"10Mb Hard Disk",type:3}]'/>
*
*
* and an example of #2 is in /configs/pc/disks/fixed/win101.xml:
*
*
* <hdc id="hdcXT" drives='[{name:"10Mb Hard Disk",path:"/disks/pc/fixed/win101/10mb.json",type:3}]'/>
*
* The HDC component expects an array of drive entries. Array position determines drive numbering
* (the first entry is drive 0, the second is drive 1, etc), and each entry contains the following
* properties:
*
*
* 'name': user-friendly name for the disk, if any
* 'path': URL of the disk image, if any
* 'type': a drive type
*
*
* Of those properties, only 'type' is required, which provides an index into an HDC "Drive Type"
* table that determines disk geometry and therefore disk size. As we add support for larger disks and
* newer disk controllers, the 'type' parameter will be superseded by either a user-defined 'geometry'
* parameter that will define number of heads, cylinders, tracks, sectors per track, and (max) bytes per
* sector, or perhaps a generic 'size' parameter that leaves geometry choices to the HDC component,
* which will then pass those decisions on to the Disk component.
*
*
* We will enable on-demand I/O for a disk image with a new 'mode' parameter that looks like:
*
*
* 'mode': one of "local", "preload", "demandrw", "demandro"
*
*
* "preload" means the disk image will be completely preloaded, exactly as before; "demandrw" enables
* full on-demand I/O support; and "demandro" enables on-demand I/O for reads only (all writes are retained
* and never written back to the server).
*
*
* "ro" will be the fallback for "rw" unless TWO other important criteria are met: 1) the user has a
* private user key, and therefore per-user storage; and 2) the disk image 'path' contains an asterisk (*)
* that the server can internally remap to a directory in the user's storage; eg:
*
*
* 'path': <asterisk>/10mb.img (path components following the asterisk are optional)
*
*
* If the disk image does not already exist, it will be created (but not formatted).
*
*
* This preserves the promise that EVERYTHING a user does within a PCjs machine is private (ie, not
* visible to any other PCjs users). I don't want to be in the business of saving any user machine
* states or disk changes, but at least those operations are limited to users who have asked for (and
* received) a private user key.
*
*
* Another important consideration at this stage is dealing with multiple machines writing to the same
* disk image; even though we're limiting the "demandrw" mode to per-user images, a single user may still
* inadvertently start up multiple machines that refer to the same disk image.
*
*
* So, every PCjs machine needs to generate a unique token and include that token with every Disk I/O API
* operation, so that the server can revoke a previous machine's "rw" access to a disk image when a new
* machine requests "rw" access to the same disk image.
*
*
* From the client's perspective, revocation can be quietly dealt with by reverting to "demandro" mode;
* that client becomes stuck with all their dirty sectors until they can reclaim "rw" access, which should
* only happen if no intervening writes to the disk image on the server have occurred (if I bother allowing
* reclamation at all).
*
*
* The real challenge here is avoiding revocation of a machine that still has critical changes to commit,
* but since we can't even solve the problem of a user closing their browser at an inopportune time
* and potentially leaving a disk image in an inconsistent state, premature revocation is the least of
@ -159,12 +159,12 @@
"use strict";
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var web = require("../../shared/lib/weblib");
var DiskAPI = require("../../shared/lib/diskapi");
var DumpAPI = require("../../shared/lib/dumpapi");
var Component = require("../../shared/lib/component");
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var web = require("../../shared/lib/weblib");
var DiskAPI = require("../../shared/lib/diskapi");
var DumpAPI = require("../../shared/lib/dumpapi");
var Component = require("../../shared/lib/component");
}
/**
@ -177,7 +177,7 @@ if (typeof module !== 'undefined') {
* @property {number} iHead
* @property {number} iModify
* @property {number} cModify
*
*
* Every Sector object (once loaded and fully parsed) should have ALL of the following named properties:
*
* 'sector': sector number
@ -186,7 +186,7 @@ if (typeof module !== 'undefined') {
* 'pattern': dword pattern to use for empty or partial sectors (or null if sector still needs to be loaded)
*
* initSector() also sets the following properties, to help us quickly identify its location within aDiskData:
*
*
* iCylinder
* iHead
*
@ -195,7 +195,7 @@ if (typeof module !== 'undefined') {
* iModify: index of first modified dword in sector
* cModify: number of modified dwords in sector
* fDirty: true if sector is dirty, false if clean (or cleaning in progress)
*
*
* fDirty is used in conjunction with "demandrw" disks; it is set to true whenever the sector is modified, and is
* set to false whenever the sector has been sent to the server. If the server write succeeds and fDirty is still
* false, then the sector modifications are removed (cModify is set to zero). If the write succeeds but fDirty was
@ -203,7 +203,7 @@ if (typeof module !== 'undefined') {
* in place (since we don't keep track of more than one modification range within a sector). And if the write failed,
* then fDirty is set back to true and again all modifications remain in place; the best we can do is schedule another
* write attempt.
*
*
* TODO: Perhaps we should also maintain a failure count and stop trying to write sectors that reach a certain
* threshold. Error-handling, as usual, is the thorniest problem.
*/
@ -223,7 +223,7 @@ if (typeof module !== 'undefined') {
*
* This means, for example, that all references to "track[iSector].data" must actually appear as
* "track[iSector]['data']".
*
*
* @constructor
* @extends Component
* @param {HDC|FDC} controller
@ -233,13 +233,13 @@ if (typeof module !== 'undefined') {
function Disk(controller, drive, mode)
{
Component.call(this, "Disk", {'id': controller.idMachine + ".disk" + Disk.nDisks++}, Disk);
this.controller = controller;
this.cmp = controller.cmp;
this.dbg = controller.dbg;
this.drive = drive;
this.mode = mode;
/*
* We pull out a number of drive properties that we may or may not need as defaults
*/
@ -258,11 +258,11 @@ function Disk(controller, drive, mode)
/*
* The following dirty sector and timer properties are used only with fOnDemand disks,
* assuming fRemote was successfully set.
* assuming fRemote was successfully set.
*/
this.aDirtySectors = [];
this.aDirtyTimestamps = []; // this array is parallel to aDirtySectors
this.timerWrite = null; // REMOTE_WRITE_DELAY timer in effect, if any
this.timerWrite = null; // REMOTE_WRITE_DELAY timer in effect, if any
this.msTimerWrite = 0; // the time that the write timer, if any, is set to fire
this.fWriteInProgress = false;
@ -276,12 +276,12 @@ function Disk(controller, drive, mode)
* @property {number} 2 contains iSector
* @property {number} 3 contains nSectors
* @property {boolean} 4 contains fAsync
* @property {function(nErrorCode:number,fAsync:boolean)} 5 contains done
* @property {function(nErrorCode:number,fAsync:boolean)} 5 contains done
*/
/**
* The default number of milliseconds to wait before writing a dirty sector back to a remote disk image
*
*
* @const {number}
*/
Disk.REMOTE_WRITE_DELAY = 2000; // 2-second delay
@ -295,7 +295,7 @@ Component.subclass(Component, Disk);
/**
* initBus(cmp, bus, cpu, dbg)
*
*
* We have no real interest in this notification, other than to obtain a reference to the Debugger
* for every disk loaded BEFORE the initBus() phase; any disk loaded AFTER that point will get its Debugger
* reference, if any, from the disk controller passed to the Disk() constructor.
@ -315,7 +315,7 @@ Disk.prototype.initBus = function(cmp, bus, cpu, dbg) {
*
* As with powerDown(), our sole concern here is for REMOTE disks: if a powerDown() call disconnected an
* "on-demand" disk, we need to get reconnected. Calling our own load() function should get the job done.
*
*
* The HDC component could have triggered this as well, but its powerUp() function only calls autoMount()
* in case of page (ie, application) reload, which is fine for local disks but insufficient for remote disks,
* which have a server connection that must re-established.
@ -339,14 +339,14 @@ Disk.prototype.powerUp = function(data, fRepower) {
*
* Our sole concern here is for REMOTE disks, making sure any unwritten changes get flushed to
* the server during a shutdown. No local state is ever returned, so fSave is ignored.
*
*
* Local disks are managed by the controller (ie, FDC or HDC) that mounted them; the controller's
* powerDown() handler will take care of calling save() as needed.
*
*
* TODO: Consider taking responsibility for saving the state of local disks as well; the only reason
* the controllers still take care of them is historical, because this component originally didn't
* exist, and even after it was created, it didn't originally receive powerDown() notifications.
*
*
* @this {Disk}
* @param {boolean} fSave
* @param {boolean} [fShutdown]
@ -385,7 +385,7 @@ Disk.prototype.powerDown = function(fSave, fShutdown)
/**
* create()
*
*
* Initializes the disk contents according to the current drive mode and parameters.
*/
Disk.prototype.create = function()
@ -407,7 +407,7 @@ Disk.prototype.create = function()
* Now that our read() and write() functions can deal with unallocated data
* arrays, and can read/write the specified pattern on-the-fly, we no longer need
* to pre-allocate and pre-initialize the 'data' array.
*
*
* For "local" disks, we can assume a 'pattern' of 0, but for "demandrw" and "demandro"
* disks, 'pattern' is set to null, as yet another indication that I/O is required to load
* the sector from the server (or to write it back to the server).
@ -429,12 +429,12 @@ Disk.prototype.create = function()
* TODO: Figure out how we can strongly type fnNotify, because the Closure Compiler has issues with:
*
* param {function(Component,Object,Disk,string,string)} fnNotify
*
*
* Also, while we're at it, learn if there are ways to:
*
*
* 1) declare a function taking NO parameters (ie, generate a warning if any parameters are specified)
* 2) declare a type for a function's return value
*
*
* @this {Disk}
* @param {string} sDiskName
* @param {string} sDiskPath
@ -443,7 +443,7 @@ Disk.prototype.create = function()
Disk.prototype.load = function(sDiskName, sDiskPath, fnNotify)
{
var sDiskURL = sDiskPath;
/*
* We could use this.log() as well, but it wouldn't also log which component initiated the load.
*/
@ -452,7 +452,7 @@ Disk.prototype.load = function(sDiskName, sDiskPath, fnNotify)
this.controller.log(sMessage);
this.messageDebugger(sMessage);
}
this.sDiskName = sDiskName;
this.sDiskPath = sDiskPath;
this.fnNotify = fnNotify;
@ -484,16 +484,16 @@ Disk.prototype.load = function(sDiskName, sDiskPath, fnNotify)
* disk BPBs, you'll always get a standard PC XT 10mb disk image, so if the 'file' or 'dir' contains
* more than 10mb of data, the request will fail. Ultimately, I want to honor the controller's
* driveConfig 'size' parm, or to match the capacity required by the driveConfig 'type' parameter.
*
*
* If a 'disk' is specified, we pass mbhd=0, because the actual size will depend on the image.
* However, I don't currently have any .DSK or .IMG files containing hard disk images; those formats
* were really intended for floppy disk images. If I never create any hard disk image files, then
* we can simply eliminate sSizeParm in the 'disk' case.
*
*
* Added more extensions to the list of paths-treated-as-disk-images, so that URLs to files located here:
*
*
* ftp://ftp.oldskool.org/pub/TOPBENCH/dskimage/
*
*
* can be used as-is. TODO: There's a TODO in netlib.getFile() regarding remote support that needs
* to be resolved first; DiskDump relies on that function for its remote requests, and it currently
* supports only HTTP.
@ -513,10 +513,10 @@ Disk.prototype.load = function(sDiskName, sDiskPath, fnNotify)
/**
* onLoadDisk(sDiskFile, sDiskData, nErrorCode, sDiskPath)
*
*
* This function was originally called mount(). If the mount is successful, we pass the Disk object to the
* caller's fnNotify handler; otherwise, we pass null.
*
*
* @this {Disk}
* @param {string} sDiskFile
* @param {string} sDiskData
@ -578,18 +578,18 @@ Disk.prototype.onLoadDisk = function(sDiskFile, sDiskData, nErrorCode, sDiskPath
* eval(). In particular, the 10Mb disk image we use for the Windows 1.01 demo config fails in
* IE9 with an "Out of memory" exception. One work-around would be to chop the data into chunks
* (perhaps one track per chunk, using regular expressions) and then manually re-assemble it.
*
*
* However, it turns out that using JSON.parse(sDiskData) instead of eval("(" + sDiskData + ")")
* is a much easier fix. The only drawback is that we must first quote any unquoted property names
* and remove any comments, because while eval() was cool with them, JSON.parse() is more particular;
* the following RegExp replacements take care of those requirements.
*
*
* The use of hex values is something else that eval() was OK with, but JSON.parse() is not, and
* while I've stopped using hex values in DumpAPI responses (at least when "format=json" is specified),
* I can't guarantee they won't show up in "legacy" images, and there's no simple RegExp replacement
* for transforming hex values into decimal values, so I cop out and fall back to eval() if I detect
* any hex prefixes ("0x") in the sequence. Ditto for error messages, which appear like so:
*
*
* ["unrecognized disk path: test.img"]
*/
var aDiskData;
@ -622,18 +622,18 @@ Disk.prototype.onLoadDisk = function(sDiskFile, sDiskData, nErrorCode, sDiskPath
* aDiskData is an array of cylinders, each of which is an array of heads, each of which
* is an array of sector objects. The format does not impose any limitations on number of
* cylinders, number of heads, or number of bytes in any of the sector object byte-arrays.
*
*
* WARNING: All accesses to sector object properties must be via their string names, not their
* "dot" names, otherwise code will break after it's been processed by the Closure Compiler.
*
*
* Sector object properties include:
*
*
* 'sector' the sector number (1-based, not required to be sequential)
* 'length' the byte-length (ie, formatted length) of the sector
* 'data' the dword-array containing the sector data
* 'pattern' if the dword-array length is less than 'length'/4, this value must be used
* to pad out the sector; if no 'pattern' is specified, it's assumed to be zero
*
*
* We still support the older JSON encoding, where sector data was encoded as an array of 'bytes'
* rather than a dword 'data' array. However, our support is strictly limited to an on-the-fly
* conversion to a forward-compatible 'data' array.
@ -698,27 +698,27 @@ Disk.prototype.onLoadDisk = function(sDiskFile, sDiskData, nErrorCode, sDiskPath
}
/*
* The current sector should now have ALL the properties of a proper Sector object; ie:
*
*
* 'sector': sector number
* 'length': size of the sector, in bytes
* 'data': array of dwords
* 'pattern': dword pattern to use for empty or partial sectors
*
*
* In addition, we will maintain the following information on a per-sector basis,
* as sectors are modified:
*
*
* iModify: index of first modified dword in sector
* cModify: number of modified dwords in sector
* fDirty: true if sector is dirty, false if clean (or cleaning in progress)
*
*
* And for the disk as a whole, we maintain a checksum of the original unmodified data:
*
*
* dwChecksum: summation of all dwords in all non-empty sectors
*/
this.initSector(sector, iCylinder, iHead);
/*
* Pattern-filling of sectors is deferred until absolutely necessary (eg, when a sector is
* being written). So all we need to do at this point is checksum all the initial sector data.
* being written). So all we need to do at this point is checksum all the initial sector data.
*/
for (var idw = 0; idw < adw.length; idw++) {
dwChecksum = (dwChecksum + adw[idw]) & 0xffffffff;
@ -741,7 +741,7 @@ Disk.prototype.onLoadDisk = function(sDiskFile, sDiskData, nErrorCode, sDiskPath
/**
* initSector(sector, iCylinder, iHead, iSector, cbSector, dwPattern)
*
*
* @param {Object} sector
* @param {number} iCylinder
* @param {number} iHead
@ -779,7 +779,7 @@ Disk.prototype.onLoadParseSectors = function(sURLName, sURLData, nErrorCode, sec
var iSector = sectorInfo[2];
var nSectors = sectorInfo[3];
fAsync = sectorInfo[4];
if (DEBUG) this.messageDebugger("Disk.onLoadParseSectors(" + iCylinder + ":" + iHead + ":" + iSector + ":" + nSectors + ")");
var abData = JSON.parse(sURLData);
@ -788,10 +788,10 @@ Disk.prototype.onLoadParseSectors = function(sURLName, sURLData, nErrorCode, sec
/*
* We call seek with fWrite == true to prevent seek() from triggering another call
* to readRemoteSectors() and endlessly recursing. That also forces seek() to:
*
*
* 1) zero the sector's 'pattern'
* 2) disable warning about reading an uninitialized sector
*
*
* We KNOW this is an uninitialized sector, because we're about to initialize it.
*/
var sector = this.seek(iCylinder, iHead, iSector, true);
@ -814,11 +814,11 @@ Disk.prototype.onLoadParseSectors = function(sURLName, sURLData, nErrorCode, sec
/**
* connectRemoteDisk(sDiskPath)
*
*
* Unlike disconnect(), we don't issue the connect request ourselves; instead, we piggyback on the existing
* preload code in load() to establish the connection. That, in turn, will trigger a call to mount(), which
* will check fOnDemand and set fRemote if the connection was successful.
*
*
* @this {Disk}
* @param {string} sDiskPath
* @return {string} is the URL connection string required to connect to sDiskPath
@ -836,7 +836,7 @@ Disk.prototype.connectRemoteDisk = function(sDiskPath)
/**
* readRemoteSectors(iCylinder, iHead, iSector, nSectors, done)
*
*
* @param {number} iCylinder
* @param {number} iHead
* @param {number} iSector
@ -847,7 +847,7 @@ Disk.prototype.connectRemoteDisk = function(sDiskPath)
Disk.prototype.readRemoteSectors = function(iCylinder, iHead, iSector, cbSector, nSectors, done)
{
if (DEBUG) this.messageDebugger("Disk.readRemoteSectors(" + iCylinder + ":" + iHead + ":" + iSector + ":" + nSectors + "," + cbSector + ")");
if (this.fRemote) {
var sParms = DiskAPI.QUERY.ACTION + '=' + DiskAPI.ACTION.READ;
sParms += '&' + DiskAPI.QUERY.VOLUME + '=' + this.sDiskPath;
@ -868,12 +868,12 @@ Disk.prototype.readRemoteSectors = function(iCylinder, iHead, iSector, cbSector,
* Writes to a remote disk are performed on a timer-driven basis. When a sector is modified for the first time,
* a reference to that sector is "pushed" onto (ie, appended to the end of) aDirtySectors, and if aDirtySectors was
* originally empty, then a REMOTE_WRITE_DELAY timer is set.
*
*
* When the timer fires, the first batch of contiguous sectors is sent off the server, and when the server responds
* (ie, when cleanDirtySectors() is called), if the response indicates success, every sector that was sent is marked
* clean -- unless one or more writes to the sector occurred in the meantime, which we track through a per-sector
* fDirty flag.
*
* fDirty flag.
*
* @param {number} iCylinder
* @param {number} iHead
* @param {number} iSector
@ -885,7 +885,7 @@ Disk.prototype.readRemoteSectors = function(iCylinder, iHead, iSector, cbSector,
Disk.prototype.writeRemoteSectors = function(iCylinder, iHead, iSector, nSectors, abSectors, fAsync)
{
if (DEBUG) this.messageDebugger("Disk.writeRemoteSectors(" + iCylinder + ":" + iHead + ":" + iSector + ":" + nSectors + ")");
if (this.fRemote) {
var data = {};
this.fWriteInProgress = true;
@ -904,7 +904,7 @@ Disk.prototype.writeRemoteSectors = function(iCylinder, iHead, iSector, nSectors
/**
* disconnectRemoteDisk()
*
*
* This is called by our powerDown() notification handler. If fRemote is true, we issue the disconnect request and
* then immediately set fRemote to false; we don't wait for (or test) the response.
*
@ -928,7 +928,7 @@ Disk.prototype.disconnectRemoteDisk = function()
*
* Mark the specified sector as dirty, add it to the queue (aDirtySectors) if not already added,
* and establish a timeout handler (findDirtySectors) if not already established.
*
*
* A freshly dirtied sector should sit in the queue for a short period of time (eg, 2 seconds)
* before we attempt to write it; that is, a REMOTE_WRITE_DELAY timer should start ticking again
* for any sector that is rewritten. However, there will be exceptions; for example, when a sector
@ -961,7 +961,7 @@ Disk.prototype.queueDirtySector = function(sector, fAsync)
*
* If a timer is already active, make sure it's still valid (ie, the time the timer is scheduled to fire is
* >= the timestamp of the next dirty sector + REMOTE_WRITE_DELAY); if not, cancel the timer and start a new one.
*
*
* @return {boolean} true if write timer set, false if not
*/
Disk.prototype.updateWriteTimer = function()
@ -1050,7 +1050,7 @@ Disk.prototype.onWriteCleanSectors = function(sURLName, sURLData, nErrorCode, se
var nSectors = sectorInfo[3];
var fAsync = sectorInfo[4];
this.fWriteInProgress = false;
if (iCylinder >= 0 && iCylinder < this.aDiskData.length && iHead >= 0 && iHead < this.aDiskData[iCylinder].length) {
for (var i = iSector - 1; nSectors-- > 0 && i >= 0 && i < this.aDiskData[iCylinder][iHead].length; i++) {
var sector = this.aDiskData[iCylinder][iHead][i];
@ -1071,7 +1071,7 @@ Disk.prototype.onWriteCleanSectors = function(sURLName, sURLData, nErrorCode, se
/**
* info()
*
*
* @this {Disk}
* @return {Array} containing: [nCylinders, nHeads, nSectorsPerTrack, nBytesPerSector]
*/
@ -1079,7 +1079,7 @@ Disk.prototype.info = function()
{
if (!this.aDiskData.length) {
return [0, 0, 0, 0];
}
}
return [this.aDiskData.length, this.aDiskData[0].length, this.aDiskData[0][0].length, this.aDiskData[0][0][0]['length']];
};
@ -1091,7 +1091,7 @@ Disk.prototype.info = function()
* Sectors that were initially compressed should remain compressed unless/until they were modified.
*
* TODO: Check sectors (or at least modified sectors) to see if they can be recompressed.
*
*
* @this {Disk}
* @return {string} containing the entire disk image as JSON-encoded data
*/
@ -1104,7 +1104,7 @@ Disk.prototype.dump = function()
* back again during mount() -- or whenever JSON.parse() is used instead of eval(). But I still remove
* them temporarily, so that any remaining property names (eg, "iModify", "cModify", "fDirty") can
* easily be stripped out, by virtue of their being the only quoted properties left. We then "requote"
* all the property names that remain.
* all the property names that remain.
*/
s = s.replace(/"(sector|length|data|pattern)":/gm, "$1:");
/*
@ -1124,17 +1124,17 @@ Disk.prototype.dump = function()
* properties. That code used to be in the FDC component, where it was perfectly reasonable
* to access those properties. We need a cleaner interface back to the drive, similar to the
* info() interface we provide to the controller.
*
*
* Whether or not the "dynamic reconfiguration" feature itself is perfectly reasonable is,
* of course, a separate question.
*
*
* @this {Disk}
* @param {number} iCylinder
* @param {number} iHead
* @param {number} iSector
* @param {boolean} [fWrite]
* @param {function(Object,boolean)} [done]
* @return {Object|null} is the requested sector, or null if not found (or not available yet)
* @return {Object|null} is the requested sector, or null if not found (or not available yet)
*/
Disk.prototype.seek = function(iCylinder, iHead, iSector, fWrite, done)
{
@ -1181,7 +1181,7 @@ Disk.prototype.seek = function(iCylinder, iHead, iSector, fWrite, done)
this.readRemoteSectors(iCylinder, iHead, iSector, drive.cbSector, nSectors, function onReadRemoteComplete(err, fAsync) {
if (err) sector = null;
// noinspection JSReferencingMutableVariableFromClosure
done(sector, fAsync);
done(sector, fAsync);
});
return null;
} else {
@ -1205,7 +1205,7 @@ Disk.prototype.seek = function(iCylinder, iHead, iSector, fWrite, done)
/**
* fill(sector, ab, off)
*
*
* @param {Object} sector
* @param {*} ab (technically, this should be typed as Array.<number> but I'm having trouble coercing JSON.parse() to that)
* @param {number} off
@ -1251,7 +1251,7 @@ Disk.prototype.toBytes = function(sector)
/**
* read(sector, ibSector, fCompare)
*
*
* @this {Disk}
* @param {Object} sector (returned from a previous seek)
* @param {number} ibSector a byte index within the given sector
@ -1261,7 +1261,7 @@ Disk.prototype.toBytes = function(sector)
Disk.prototype.read = function(sector, ibSector, fCompare)
{
var b = -1;
if (DEBUG && !ibSector && !fCompare) this.messageDebugger("Disk.read(" + this.controller.id + ":" + this.drive.iDrive + "," + sector.iCylinder + ":" + sector.iHead + ":" + sector['sector'] + ")");
if (ibSector < sector['length']) {
@ -1275,7 +1275,7 @@ Disk.prototype.read = function(sector, ibSector, fCompare)
/**
* write(sector, ibSector, b)
*
*
* @this {Disk}
* @param {Object} sector (returned from a previous seek)
* @param {number} ibSector a byte index within the given sector
@ -1286,7 +1286,7 @@ Disk.prototype.write = function(sector, ibSector, b)
{
if (this.fWriteProtected)
return false;
if (DEBUG && !ibSector) this.messageDebugger("Disk.write(" + this.controller.id + ":" + this.drive.iDrive + "," + sector.iCylinder + ":" + sector.iHead + ":" + sector['sector'] + ")");
if (ibSector < sector['length']) {
@ -1299,7 +1299,7 @@ Disk.prototype.write = function(sector, ibSector, b)
* Ensure every byte up to the specified byte is properly initialized.
*/
for (var i = adw.length; i <= idw; i++) adw[i] = dwPattern;
if (!sector.cModify) {
sector.iModify = idw;
sector.cModify = 1;
@ -1330,7 +1330,7 @@ Disk.prototype.write = function(sector, ibSector, b)
* [iCylinder, iHead, iSector, iModify, [...]]
*
* where [...] is an array of modified dword(s) in the corresponding sector.
*
*
* @this {Disk}
* @return {Array} of modified sectors
*/
@ -1373,7 +1373,7 @@ Disk.prototype.save = function()
* [iCylinder, iHead, iSector, iModify, [...]]
*
* where [...] is an array of modified dword(s) in the corresponding sector.
*
*
* @this {Disk}
* @param {Array} deltas
* @return {number} 0 if no changes applied, -1 if an error occurred, otherwise the number of sectors modified
@ -1382,7 +1382,7 @@ Disk.prototype.restore = function(deltas)
{
/*
* If deltas is undefined, that's not necessarily an error; the controller may simply be (re)initializing
* itself (although neither controller should be calling restore() under those conditions anymore).
* itself (although neither controller should be calling restore() under those conditions anymore).
*/
var nChanges = 0;
var sReason = "unsupported restore format";
@ -1426,7 +1426,7 @@ Disk.prototype.restore = function(deltas)
* Note the buried test for write-protection. Yes, an invariant condition should be tested
* outside the loop, not inside, but (a) it's a trivial test, (b) the test should never fail
* because save() should never generate any mods for a write-protected disk, and (c) it
* centralizes all the failure conditions we're currently checking (which, admittedly, ain't much).
* centralizes all the failure conditions we're currently checking (which, admittedly, ain't much).
*/
if (iCylinder >= this.aDiskData.length || iHead >= this.aDiskData[iCylinder].length || iSector >= this.aDiskData[iCylinder][iHead].length) {
sReason = "sector " + iCylinder + ":" + iHead + ":" + iSector + " out of range (" + nChanges + " changes applied)";
@ -1471,7 +1471,7 @@ Disk.prototype.restore = function(deltas)
* messageDebugger(sMessage)
*
* This is a combination of the Debugger's messageEnabled(MESSAGE_DISK) and message() functions, for convenience.
*
*
* @this {Disk}
* @param {string} sMessage is any caller-defined message string
*/

View file

@ -34,14 +34,14 @@
"use strict";
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var DiskAPI = require("../../shared/lib/diskapi");
var Component = require("../../shared/lib/component");
var ChipSet = require("./chipset");
var Disk = require("./disk");
var Computer = require("./computer");
var State = require("./state");
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var DiskAPI = require("../../shared/lib/diskapi");
var Component = require("../../shared/lib/component");
var ChipSet = require("./chipset");
var Disk = require("./disk");
var Computer = require("./computer");
var State = require("./state");
}
/*
@ -738,6 +738,13 @@ FDC.prototype.initController = function(data)
for (iDrive = 0; iDrive < this.aDrives.length; iDrive++) {
var drive = this.aDrives[iDrive];
if (drive === undefined) {
/*
* The first time each drive is initialized, obtain its type (from switches or CMOS) and the physical limits
* of the drive (ie, max tracks and max sectors/track). As for max heads, initDrive() assumes that all drives
* have two heads, and in any case, there's no way to configure/specify a single-sided floppy drive.
*
* TODO: Provide a configuration option for single-sided drives, in case someone really wants to simulate that.
*/
drive = this.aDrives[iDrive] = {};
drive.bType = this.chipset.getSWFloppyDriveType(iDrive);
drive.nCylinders = 40;
@ -823,7 +830,7 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
/*
* Note that when no data is provided (eg, when the controller is being reinitialized), we now take
* care to preserve any drive defaults that initController() already obtained for us, falling back to
* bare minimums only when all else has failed.
* bare minimums only when all else fails.
*/
data[1] = [FDC.DEFAULT_DRIVE_NAME, drive.nCylinders || 40, drive.nHeads || data[3], drive.nSectors || 9, drive.cbSector || 512, data[1]];
}
@ -845,6 +852,18 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
drive.nSectors = data[i][3]; // sectors/track
drive.cbSector = data[i][4]; // bytes/sector
drive.fRemovable = data[i][5];
/*
* If we have current media parameters, restore them; otherwise, default to the drive's physical parameters.
*/
if (drive.nDiskCylinders = data[i][6]) {
drive.nDiskHeads = data[i][7];
drive.nDiskSectors = data[i][8];
} else {
drive.nDiskCylinders = drive.nCylinders;
drive.nDiskHeads = drive.nHeads;
drive.nDiskSectors = drive.nSectors;
}
i++;
/*
@ -888,11 +907,7 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
drive.nBytes = data[i++];
/*
* The next group of properties are set by user requests to load/unload diskette images.
*
* NOTE: I now avoid reinitializing drive.disk in order to retain any previously mounted diskette across resets.
*
* drive.disk = null; // when a "disk" is "inserted" into the "drive", this is a Disk object
* We no longer reinitialize drive.disk, in order to retain previously mounted diskette across resets.
*/
/*
@ -971,7 +986,7 @@ FDC.prototype.saveDrive = function(drive)
var i = 0;
var data = [];
data[i++] = drive.resCode;
data[i++] = [drive.name, drive.nCylinders, drive.nHeads, drive.nSectors, drive.cbSector, drive.fRemovable];
data[i++] = [drive.name, drive.nCylinders, drive.nHeads, drive.nSectors, drive.cbSector, drive.fRemovable, drive.nDiskCylinders, drive.nDiskHeads, drive.nDiskSectors];
data[i++] = drive.bHead;
/*
* We used to store drive.nHeads in the next slot, but now we store bCylinderSeek,
@ -1170,15 +1185,20 @@ FDC.prototype.loadDiskette = function(iDrive, sDisketteName, sDiskettePath, fAut
*/
FDC.prototype.mountDiskette = function(drive, disk, sDisketteName, sDiskettePath)
{
var aDiskInfo;
drive.fBusy = false;
/*
* We shouldn't mount the diskette unless the drive is able to handle it; for example, DSDD (40-track)
* drives cannot read DSHD (80-track) diskettes.
*/
if (disk && disk.aDiskData.length > drive.nCylinders) {
this.notice("Diskette \"" + sDisketteName + "\" too large for drive " + String.fromCharCode(0x41 + drive.iDrive));
disk = null;
if (disk) {
/*
* We shouldn't mount the diskette unless the drive is able to handle it; for example, DSDD (40-track)
* drives cannot read DSHD (80-track) diskettes.
*/
aDiskInfo = disk.info();
if (disk && aDiskInfo[0] > drive.nCylinders || aDiskInfo[1] > drive.nHeads || aDiskInfo[2] > drive.nSectors) {
this.notice("Diskette \"" + sDisketteName + "\" too large for drive " + String.fromCharCode(0x41 + drive.iDrive));
disk = null;
}
}
if (disk) {
@ -1205,6 +1225,13 @@ FDC.prototype.mountDiskette = function(drive, disk, sDisketteName, sDiskettePath
* and will not match the drive mappings that DOS ultimately uses (ie, for drives beyond B:).
*/
this.notice("Mounted diskette \"" + sDisketteName + "\" in drive " + String.fromCharCode(0x41 + drive.iDrive), drive.fAutoMount);
/*
* Update the drive's current media parameters to match the disk's.
*/
drive.nDiskCylinders = aDiskInfo[0];
drive.nDiskHeads = aDiskInfo[1];
drive.nDiskSectors = aDiskInfo[2];
}
if (drive.fAutoMount) {
@ -2141,8 +2168,8 @@ FDC.prototype.writeByte = function(drive, b)
/**
* advanceSector(drive)
*
* This increments the sector number; when the sector number reaches drive.nSectors on the current track, we
* increment drive.bHead and reset drive.bSector, and when drive.bHead reaches drive.nHeads, we reset drive.bHead
* This increments the sector number; when the sector number reaches drive.nDiskSectors on the current track, we
* increment drive.bHead and reset drive.bSector, and when drive.bHead reaches drive.nDiskHeads, we reset drive.bHead
* and increment drive.bCylinder.
*
* @this {FDC}
@ -2150,13 +2177,13 @@ FDC.prototype.writeByte = function(drive, b)
*/
FDC.prototype.advanceSector = function(drive)
{
Component.assert(drive.bCylinder < drive.nCylinders);
Component.assert(drive.bCylinder < drive.nDiskCylinders);
drive.bSector++;
var bSectorStart = 1;
if (drive.bSector >= drive.nSectors + bSectorStart) {
if (drive.bSector >= drive.nDiskSectors + bSectorStart) {
drive.bSector = bSectorStart;
drive.bHead++;
if (drive.bHead >= drive.nHeads) {
if (drive.bHead >= drive.nDiskHeads) {
drive.bHead = 0;
drive.bCylinder++;
}

View file

@ -34,13 +34,13 @@
"use strict";
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var DiskAPI = require("../../shared/lib/diskapi");
var Component = require("../../shared/lib/component");
var ChipSet = require("./chipset");
var Disk = require("./disk");
var State = require("./state");
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var DiskAPI = require("../../shared/lib/diskapi");
var Component = require("../../shared/lib/component");
var ChipSet = require("./chipset");
var Disk = require("./disk");
var State = require("./state");
}
/**
@ -145,7 +145,7 @@ HDC.aDriveTypes = [
* so aDriveTypes must first be indexed by a controller index (this.iHDC).
*
* The following is a more complete description of the drive types supported by the MODEL_5170, where C is
* Cylinders, H is Heads, WP is Write Pre-Comp, and LZ is Landing Zone (in practice, we don't need WP or LZ).
* Cylinders, H is Heads, WP is Write Pre-Comp, and LZ is Landing Zone (in practice, we don't need WP or LZ).
*
* Type C H WP LZ
* ---- --- -- --- ---
@ -457,7 +457,7 @@ HDC.BIOS = {
};
/*
* NOTE: These are useful values for reference, but they're not actually used for anything at the moment.
* NOTE: These are useful values for reference, but they're not actually used for anything at the moment.
*/
HDC.BIOS.DISK_CMD = {
RESET: 0x00,
@ -865,7 +865,7 @@ HDC.prototype.initDrive = function(iDrive, drive, driveConfig, data, fReset)
/*
* The next group of properties are set by user requests to load/unload disk images.
*
* NOTE: I now avoid reinitializing drive.disk in order to retain any previously mounted disk across resets.
* We no longer reinitialize drive.disk, in order to retain previously mounted disk across resets.
*/
if (drive.disk === undefined) {
drive.disk = null;
@ -1235,7 +1235,7 @@ HDC.prototype.outXTCData = function(port, bOut, addrFrom)
}
if (this.regDataTotal >= cbCmd) {
/*
* It's essential that XTC.STATUS.IOMODE be set here, at least after the final 8-byte HDC.XTC.DATA.CMD.INIT_DRIVE sequence.
* It's essential that XTC.STATUS.IOMODE be set here, at least after the final 8-byte HDC.XTC.DATA.CMD.INIT_DRIVE sequence.
*/
this.regStatus |= HDC.XTC.STATUS.IOMODE;
this.regStatus &= ~HDC.XTC.STATUS.REQ;
@ -1650,13 +1650,13 @@ HDC.prototype.outATCDrvHd = function(port, bOut, addrFrom)
* of configured hard drives is something other than 2, using INT 0x13/AH=0x10. This in turn calls the
* BIOS "TST_RDY" function, which selects the drive in this register (see DRIVE_MASK), and then immediately
* expects regStatus to reflect success or failure.
*
*
* We were always returning success, because no ATC command was actually issued, and so the user would
* always get a spurious CMOS configuration error: "System Options Not Set-(Run SETUP)".
*
*
* So now we update regStatus here. I'm not sure which status bits are normally set to indicate failure,
* but it should be sufficient to set or clear the READY bit according to whether the drive exists or not.
*
*
* TODO: Dig into the ATC documentation some more, and determine what other situations, if any, regStatus
* needs to be updated.
*/

View file

@ -34,12 +34,12 @@
"use strict";
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var ChipSet = require("./chipset");
var State = require("./state");
var CPU = require("./cpu");
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var ChipSet = require("./chipset");
var State = require("./state");
var CPU = require("./cpu");
}
/**
@ -57,7 +57,7 @@ if (typeof module !== 'undefined') {
*
* Its main purpose is to receive binding requests for various keyboard events,
* and to use those events to simulate the PC's keyboard hardware.
*
*
* @constructor
* @extends Component
* @param {Object} parmsKbd
@ -119,11 +119,11 @@ Keyboard.CMD.SETLEDS = 0xED;
Keyboard.CMDRES = {};
Keyboard.CMDRES.OVERRUN = 0x00;
Keyboard.CMDRES.LOADTEST = 0x65; // this is an undocumented "LOAD MANUFACTURING TEST REQUEST" response code
Keyboard.CMDRES.BATSUCCESS = 0xAA; // Basic Assurance Test (BAT) completed successfully
Keyboard.CMDRES.BATSUCCESS = 0xAA; // Basic Assurance Test (BAT) completed successfully
Keyboard.CMDRES.ECHO = 0xEE;
Keyboard.CMDRES.BREAKPREFIX = 0xF0;
Keyboard.CMDRES.ACK = 0xFA;
Keyboard.CMDRES.BATFAIL = 0xFC; // Basic Assurance Test (BAT) failed
Keyboard.CMDRES.BATFAIL = 0xFC; // Basic Assurance Test (BAT) failed
Keyboard.CMDRES.DIAGFAIL = 0xFD;
Keyboard.CMDRES.RESEND = 0xFE;
@ -280,7 +280,7 @@ Keyboard.aButtonCodes = {
* For example, Safari on iOS devices will not generate up/down events for shift keys, and for other keys,
* the up/down events are usually generated after the actual press is complete, and in rapid succession,
* which doesn't always give the simulation enough time to detect the key.
*
*
* The other problem (which is more of a problem with keyboards like the C1P than any IBM keyboards) is
* that the shift/modifier state for a character on the "source" keyboard may not match the shift/modifier
* state for the same character on the "target" keyboard. And since this code is inherited from C1Pjs,
@ -424,7 +424,7 @@ Keyboard.aCharCodes[Keyboard.CHARCODE.CTRLALTDEL]=0x53 + (Keyboard.CHARCODE.CTRL
/**
* setBinding(sHTMLClass, sHTMLType, sBinding, control)
*
*
* @this {Keyboard}
* @param {string|null} sHTMLClass is the class of the HTML control (eg, "input", "output")
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
@ -444,7 +444,7 @@ Keyboard.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, contro
*
* However, it's also possible for the keyboard XML definition to define a control that serves
* a similar purpose; eg:
*
*
* <control class="input" type="text" binding="kbd" width="2em">Kbd</control>
*
* The latter is purely experimental, while we work on finding ways to trigger the soft keyboard on
@ -508,7 +508,7 @@ Keyboard.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, contro
/**
* findBinding(bKey, t, fDown)
*
*
* @this {Keyboard}
* @param {number} bKey
* @param {string} t is the type of control (eg, "button" or "key")
@ -533,7 +533,7 @@ Keyboard.prototype.findBinding = function(bKey, t, fDown)
/**
* initBus(cmp, bus, cpu, dbg)
*
*
* @this {Keyboard}
* @param {Computer} cmp
* @param {Bus} bus
@ -551,7 +551,7 @@ Keyboard.prototype.initBus = function(cmp, bus, cpu, dbg)
/**
* setModel(nModel)
*
*
* @this {Keyboard}
* @param {number} nModel
*/
@ -561,7 +561,7 @@ Keyboard.prototype.setModel = function(nModel)
/**
* setReady()
*
*
* @this {Keyboard}
*/
Keyboard.prototype.setReady = function()
@ -574,7 +574,7 @@ Keyboard.prototype.setReady = function()
/**
* resetDevice()
*
*
* @this {Keyboard}
*/
Keyboard.prototype.resetDevice = function()
@ -589,12 +589,12 @@ Keyboard.prototype.resetDevice = function()
/**
* setEnable(fData, fClock)
*
*
* This is the ChipSet's primary interface for toggling keyboard "data" and "clock" lines.
* For MODEL_5150 and MODEL_5160 machines, this function is called from the ChipSet's PPI_B
* output handler. For MODEL_5170 machines, this function is called when selected KBC.CMD
* "data bytes" have been written.
*
*
* @this {Keyboard}
* @param {boolean} fData is true if the keyboard simulated data line should be enabled
* @param {boolean} fClock is true if the keyboard's simulated clock line should be enabled
@ -635,7 +635,7 @@ Keyboard.prototype.setEnable = function(fData, fClock)
*
* This is the ChipSet's primary interface for controlling "Model M" keyboards (ie, those used
* with MODEL_5170 machines). Commands are delivered through the ChipSet's 8042 Keyboard Controller.
*
*
* @this {Keyboard}
* @param {number} bCmd should be one of the Keyboard.CMD.* command codes (Model M keyboards only)
* @return {number} response should be one of the Keyboard.CMDRES.* response codes, or -1 if unrecognized
@ -656,7 +656,7 @@ Keyboard.prototype.sendCmd = function(bCmd)
/**
* readScanCode(fShift)
*
*
* This is the ChipSet's interface for reading scan codes.
*
* @this {Keyboard}
@ -676,9 +676,9 @@ Keyboard.prototype.readScanCode = function(fShift)
/**
* shiftScanCode(fFlush)
*
*
* This is the ChipSet's interface to advance (or flush) scan codes.
*
*
* @this {Keyboard}
* @param {boolean} [fFlush] is true to completely flush the keyboard buffer
*/
@ -736,7 +736,7 @@ Keyboard.prototype.powerUp = function(data, fRepower)
/**
* powerDown(fSave)
*
*
* @this {Keyboard}
* @param {boolean} fSave
* @return {Object|boolean}
@ -748,7 +748,7 @@ Keyboard.prototype.powerDown = function(fSave)
/**
* reset()
*
*
* @this {Keyboard}
*/
Keyboard.prototype.reset = function()
@ -801,7 +801,7 @@ Keyboard.prototype.reset = function()
* save()
*
* This implements save support for the Keyboard component.
*
*
* @this {Keyboard}
* @return {Object}
*/
@ -816,7 +816,7 @@ Keyboard.prototype.save = function()
* restore(data)
*
* This implements restore support for the Keyboard component.
*
*
* @this {Keyboard}
* @param {Object} data
* @return {boolean} true if successful, false if failure
@ -828,7 +828,7 @@ Keyboard.prototype.restore = function(data)
/**
* initState(data)
*
*
* @this {Keyboard}
* @param {Array} [data]
* @return {boolean} true if successful, false if failure
@ -844,7 +844,7 @@ Keyboard.prototype.initState = function(data)
/**
* saveState()
*
*
* @this {Keyboard}
* @return {Array}
*/
@ -859,7 +859,7 @@ Keyboard.prototype.saveState = function()
/**
* setSoftKeyState(control, f)
*
*
* @this {Keyboard}
* @param {Object} control is an HTML control DOM object
* @param {boolean} f is true if the key represented by e should be "on", false if "off"
@ -877,7 +877,7 @@ Keyboard.prototype.setSoftKeyState = function(control, f)
*
* Just as 0xAA is a special scan code response to a software reset, 0xFF is a special scan code response
* to an internal buffer overrun. I try to simulate both.
*
*
* @this {Keyboard}
* @param {number} bScan
* @param {boolean} [fRepeat]
@ -917,7 +917,7 @@ Keyboard.prototype.addScanCode = function(bScan, fRepeat)
/**
* calcReleaseDelay(fRepeat)
*
*
* Attempts to scale our default "release" delay appropriately for the current CPU speed.
*
* Note that if the effective CPU speed exceeds 16Mhz, it becomes very difficult to rely on timer-driven key events
@ -947,7 +947,7 @@ Keyboard.prototype.calcReleaseDelay = function(fRepeat)
/**
* autoClear(notCharCode)
*
*
* @this {Keyboard}
* @param {number} [notCharCode]
*/
@ -963,7 +963,7 @@ Keyboard.prototype.autoClear = function(notCharCode)
/**
* injectKeys(sKeyCodes, msDelay)
*
*
* @this {Keyboard}
* @param {string} sKeyCodes
* @param {number|undefined} [msDelay] is an optional injection delay (default is msInjectDelay)
@ -977,7 +977,7 @@ Keyboard.prototype.injectKeys = function(sKeyCodes, msDelay)
/**
* injectKeysFromBuffer(msDelay)
*
*
* @this {Keyboard}
* @param {number} msDelay is the delay between injected keys
*/
@ -1004,7 +1004,7 @@ Keyboard.prototype.injectKeysFromBuffer = function(msDelay)
/**
* keyEvent(event, fDown)
*
*
* @this {Keyboard}
* @param {Object} event
* @param {boolean} fDown is true if called for a keyDown event, false if called for a keyUp event
@ -1070,7 +1070,7 @@ Keyboard.prototype.keyEvent = function(event, fDown)
* which also generates "down" and "up" events (LOTS of "down" events for that matter),
* but no "press" event. The C1P has no TAB key, so it's safe to completely ignore,
* hence the code below, but a PC does, so I need to simulate it.
*
*
* fPass = fAutoClear = false;
*
* I don't get keyPress events for ESC (why?) and I never want the browser to act on DELETE
@ -1117,10 +1117,10 @@ Keyboard.prototype.keyEvent = function(event, fDown)
* "up" events, so that keys will repeat immediately when released/pressed repeatedly (most
* noticeable with the Enter key), or set fAutoClear to false to ensure that polling apps have
* enough time to see every key press.
*
*
* I've decided that the former is more important than the latter, so if polling apps are still
* missing keystrokes, then perhaps nCyclesThreshold needs to be supplemented in some way.
*
*
* fAutoClear = false;
*/
}
@ -1161,7 +1161,7 @@ Keyboard.prototype.keyEvent = function(event, fDown)
*
* We've stopped relying on keyPress for keyboard emulation purposes, but it's still handy to hook and monitor
* when debugging.
*
*
* @this {Keyboard}
* @param {Object} event
* @return {boolean} true to pass the event along, false to consume it
@ -1184,12 +1184,12 @@ Keyboard.prototype.keyPress = function(event)
/*
* Unlike Safari and Chrome, Firefox doesn't seem to honor our "consume" request for the "down" DELETE keyEvent,
* so we must ALSO check for the DELETE key here, and again "consume" it. Ditto for TAB.
*
*
* In fact, this is just one example of a larger Firefox problem (see https://bugzilla.mozilla.org/show_bug.cgi?id=501496).
* Basically, Firefox is not honoring our consumption of keyDown events, and generates keyPress events anyway.
* This causes us grief for various CTRL and ALT combinations, resulting in duplicate key presses.
* So, I'm going to try to fix this below, by setting fPass to true if either of those modifier keys is currently down;
* if they're not, then we'll continue with the original code that sets fPass based on the return value from keyPressSimulate().
* if they're not, then we'll continue with the original code that sets fPass based on the return value from keyPressSimulate().
*/
fPass = false;
} else {
@ -1210,7 +1210,7 @@ Keyboard.prototype.keyPress = function(event)
/**
* keyPressSimulate(charCode)
*
*
* @this {Keyboard}
* @param {number} charCode
* @param {boolean} [fQuickRelease] is true to simulate the press and release immediately
@ -1232,10 +1232,10 @@ Keyboard.prototype.keyPressSimulate = function(charCode, fQuickRelease)
* and execute a LOT of instructions between delivery of the keyPress event and the "keyTimeout"
* event, and since JavaScript events (including timeouts) are delivered synchronously, it might
* take too long for the "keyTimeout" event to arrive.
*
*
* Why don't we ALWAYS do this? Because at normal CPU speeds, we want to faithfully simulate how
* long a key is held, so that features like auto-repeat work properly.
*
*
* TODO: The above is probably more true for C1Pjs (where some of this code came from) than PCjs,
* so revisit these assumptions. The fact that I had to add the fQuickRelease parameter suggests
* that it's time to review/overhaul this code.
@ -1265,7 +1265,7 @@ Keyboard.prototype.keyPressSimulate = function(charCode, fQuickRelease)
/**
* keyEventSimulate(charCode, fDown, simCode)
*
*
* @this {Keyboard}
* @param {number} charCode
* @param {boolean} fDown
@ -1353,7 +1353,7 @@ Keyboard.prototype.keyEventSimulate = function(charCode, fDown, simCode)
* messageDebugger(sMessage, fPort)
*
* This is a combination of the Debugger's messageEnabled(MESSAGE_KBD) and message() functions, for convenience.
*
*
* @this {Keyboard}
* @param {string} sMessage is any caller-defined message string
* @param {boolean} [fPort] is true if the message is port-related, false if not

View file

@ -50,8 +50,8 @@
"use strict";
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var Component = require("../../shared/lib/component");
var str = require("../../shared/lib/strlib");
var Component = require("../../shared/lib/component");
}
/**
* @class DataView
@ -70,7 +70,7 @@ if (typeof module !== 'undefined') {
* block-granular starting address and an address range equal to bus.blockSize; however,
* the size of any given Memory object's underlying buffer can be either zero or bus.blockSize;
* memory read/write functions for empty (buffer-less) blocks are mapped to readNone/writeNone.
*
*
* The Bus allocates empty blocks for the entire address space during initialization, so that
* any reads/writes to undefined addresses will have no effect. Later, the ROM and RAM
* components will ask the Bus to allocate memory for specific ranges, and the Bus will allocate
@ -90,7 +90,7 @@ if (typeof module !== 'undefined') {
* consumption. Using TYPEDARRAYS is probably best, although not all JavaScript implementations
* support them (IE9 is probably the only real outlier: it lacks typed arrays but otherwise has
* all the necessary HTML5 support).
*
*
* @constructor
* @param {number} addr of block (must be some multiple of bus.blockSize)
* @param {number} [size] of block's buffer in bytes (0 for none); must be a multiple of 4
@ -107,13 +107,13 @@ function Memory(addr, size, fReadOnly, controller) {
/*
* For empty memory blocks, all we need to do is ensure all access functions
* are mapped to "none" handlers.
* are mapped to "none" handlers.
*/
if (!size) {
this.setAccess();
return;
}
/*
* When a controller is specified, the controller must provide a buffer,
* via getMemoryBuffer(), and memory access functions, via getMemoryAccess().
@ -126,7 +126,7 @@ function Memory(addr, size, fReadOnly, controller) {
this.setAccess(controller.getMemoryAccess());
return;
}
/*
* This is the normal case: allocate a buffer that provides 8 bits of data per address;
* no controller is required because our default memory access functions (see afnMemory)
@ -161,7 +161,7 @@ Memory.prototype = {
constructor: Memory,
/**
* readNone(off)
*
*
* @this {Memory}
* @param {number} off
* @return {number}
@ -174,7 +174,7 @@ Memory.prototype = {
},
/**
* writeNone(off, v)
*
*
* @this {Memory}
* @param {number} off
* @param {number} v (could be either a byte or word value, since we use the same handler for both kinds of accesses)
@ -186,7 +186,7 @@ Memory.prototype = {
},
/**
* readByteTypedArray(off)
*
*
* @this {Memory}
* @param {number} off
* @return {number}
@ -197,7 +197,7 @@ Memory.prototype = {
},
/**
* readWordTypedArray(off)
*
*
* @this {Memory}
* @param {number} off
* @return {number}
@ -208,7 +208,7 @@ Memory.prototype = {
},
/**
* writeByteTypedArray(off, b)
*
*
* @this {Memory}
* @param {number} off
* @param {number} b
@ -220,7 +220,7 @@ Memory.prototype = {
},
/**
* writeWordTypedArray(off, w)
*
*
* @this {Memory}
* @param {number} off
* @param {number} w
@ -232,7 +232,7 @@ Memory.prototype = {
},
/**
* readByteMemory(off)
*
*
* @this {Memory}
* @param {number} off
* @return {number}
@ -246,7 +246,7 @@ Memory.prototype = {
},
/**
* readWordMemory(off)
*
*
* @this {Memory}
* @param {number} off
* @return {number}
@ -269,7 +269,7 @@ Memory.prototype = {
},
/**
* writeByteMemory(off, b)
*
*
* @this {Memory}
* @param {number} off
* @param {number} b
@ -287,7 +287,7 @@ Memory.prototype = {
},
/**
* writeWordMemory(off, w)
*
*
* @this {Memory}
* @param {number} off
* @param {number} w
@ -312,7 +312,7 @@ Memory.prototype = {
},
/**
* readByteVerify(off)
*
*
* @this {Memory}
* @param {number} off
* @return {number}
@ -323,7 +323,7 @@ Memory.prototype = {
},
/**
* readWordVerify(off)
*
*
* @this {Memory}
* @param {number} off
* @return {number}
@ -336,7 +336,7 @@ Memory.prototype = {
},
/**
* writeByteVerify(off, b)
*
*
* @this {Memory}
* @param {number} off
* @param {number} b
@ -347,7 +347,7 @@ Memory.prototype = {
},
/**
* writeWordVerify(off, w)
*
*
* @this {Memory}
* @param {number} off
* @param {number} w
@ -366,7 +366,7 @@ Memory.prototype = {
*
* Memory blocks with custom memory controllers do NOT save their contents;
* that's the responsibility of the controller component.
*
*
* @this {Memory}
* @return {Array|Int32Array|null}
*/
@ -389,7 +389,7 @@ Memory.prototype = {
* but we can't be sure of the "endianness" of an Int32Array -- which would be OK if the array
* was always saved/restored on the same machine, but there's no guarantee of that, either.
* So we use getInt32() and require little-endian values.
*
*
* Moreover, an Int32Array isn't treated by JSON.stringify() and JSON.parse() exactly like
* a normal array; it's serialized as an Object rather than an Array, so it lacks a "length"
* property and causes problems for State.store() and State.parse().
@ -411,7 +411,7 @@ Memory.prototype = {
* used by Bus.restoreMemory(), which is called by X86CPU.restore(), after all other
* components have been restored and thus all Memory blocks have been allocated
* by their respective components.
*
*
* @this {Memory}
* @param {Array|null} adw
* @return {boolean} true if successful, false if block size mismatch
@ -445,7 +445,7 @@ Memory.prototype = {
},
/**
* setAccess(afn)
*
*
* @this {Memory}
* @param {Array.<function()>} [afn]
* @param {boolean} [fDirect]
@ -458,7 +458,7 @@ Memory.prototype = {
},
/**
* setReadAccess(afn, fDirect)
*
*
* @this {Memory}
* @param {Array.<function()>} afn
* @param {boolean} [fDirect]
@ -473,7 +473,7 @@ Memory.prototype = {
},
/**
* setWriteAccess(afn, fDirect)
*
*
* @this {Memory}
* @param {Array.<function()>} afn
* @param {boolean} [fDirect]
@ -488,7 +488,7 @@ Memory.prototype = {
},
/**
* resetReadAccess()
*
*
* @this {Memory}
*/
resetReadAccess: function() {
@ -497,7 +497,7 @@ Memory.prototype = {
},
/**
* resetWriteAccess()
*
*
* @this {Memory}
*/
resetWriteAccess: function() {
@ -506,7 +506,7 @@ Memory.prototype = {
},
/**
* setDebugInfo(cpu, dbg, addr, size)
*
*
* @this {Memory}
* @param {X86CPU|Component} cpu
* @param {Debugger|Component} dbg
@ -524,7 +524,7 @@ Memory.prototype = {
},
/**
* addBreakpoint(off, fWrite)
*
*
* @this {Memory}
* @param {number} off
* @param {boolean} fWrite
@ -547,7 +547,7 @@ Memory.prototype = {
},
/**
* removeBreakpoint(off, fWrite)
*
*
* @this {Memory}
* @param {number} off
* @param {boolean} fWrite

View file

@ -34,11 +34,11 @@
"use strict";
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var SerialPort = require("./serial");
var State = require("./state");
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var SerialPort = require("./serial");
var State = require("./state");
}
/**
@ -64,7 +64,7 @@ if (typeof module !== 'undefined') {
* TODO: Just out of curiosity, verify that the Microsoft Bus Mouse used ports 0x23D and 0x23F,
* because I saw Windows v1.01 probing those ports immediately prior to probing COM2 (and then COM1)
* for a serial mouse.
*
*
* @constructor
* @extends Component
* @param {Object} parmsMouse
@ -83,14 +83,14 @@ function Mouse(parmsMouse) {
/*
* From http://paulbourke.net/dataformats/serialmouse:
*
*
* The old MicroSoft serial mouse, while no longer in general use, can be employed to provide a low cost input device,
* for example, coupling the internal mechanism to other moving objects. The serial protocol for the mouse is:
*
* 1200 baud, 7 bit, 1 stop bit, no parity.
*
* The pinout of the connector follows the standard serial interface, as shown below:
*
*
* Pin Abbr Description
* 1 DCD Data Carrier Detect
* 2 RD Receive Data [serial data from mouse to host]
@ -101,7 +101,7 @@ function Mouse(parmsMouse) {
* 7 RTS Request To Send [used to provide positive voltage to mouse]
* 8 CTS Clear To Send
* 9 RI Ring
*
*
* Every time the mouse changes state (moved or button pressed) a three byte "packet" is sent to the serial interface.
* For reasons known only to the engineers, the data is arranged as follows, most notably the two high order bits for the
* x and y coordinates share the first byte with the button status.
@ -110,16 +110,16 @@ function Mouse(parmsMouse) {
* 1st byte 1 LB RB Y7 Y6 X7 X6
* 2nd byte 0 X5 X4 X3 X2 X1 X0
* 3rd byte 0 Y5 Y4 Y3 Y2 Y1 Y0
*
*
* where:
*
*
* LB is the state of the left button, 1 = pressed, 0 = released.
* RB is the state of the right button, 1 = pressed, 0 = released
* X0-7 is movement of the mouse in the X direction since the last packet. Positive movement is toward the right.
* Y0-7 is movement of the mouse in the Y direction since the last packet. Positive movement is back, toward the user.
*
*
* From http://www.kryslix.com/nsfaq/Q.12.html:
*
*
* The Microsoft serial mouse is the most popular 2-button mouse. It is supported by all major operating systems.
* The maximum tracking rate for a Microsoft mouse is 40 reports/second * 127 counts per report, in other words, 5080 counts
* per second. The most common range for mice is is 100 to 400 CPI (counts per inch) but can be up to 1000 CPI. A 100 CPI mouse
@ -141,12 +141,12 @@ function Mouse(parmsMouse) {
* (0x4D, ASCII 'M').
*
* Serial data parameters: 1200bps, 7 data bits, 1 stop bit
*
*
* Data is sent in 3 byte packets for each event (a button is pressed or released, or the mouse moves):
*
*
* D7 D6 D5 D4 D3 D2 D1 D0
* Byte 1 X 1 LB RB Y7 Y6 X7 X6
* Byte 2 X 0 X5 X4 X3 X2 X1 X0
* Byte 2 X 0 X5 X4 X3 X2 X1 X0
* Byte 3 X 0 Y5 Y4 Y3 Y2 Y1 Y0
*
* LB is the state of the left button (1 means down).
@ -161,7 +161,7 @@ Component.subclass(Component, Mouse);
/**
* initBus(cmp, bus, cpu, dbg)
*
*
* @this {Mouse}
* @param {Computer} cmp
* @param {Bus} bus
@ -180,7 +180,7 @@ Mouse.prototype.initBus = function(cmp, bus, cpu, dbg) {
/**
* isActive()
*
*
* @this {Mouse}
* @return {boolean} true if active, false if not
*/
@ -214,10 +214,10 @@ Mouse.prototype.powerUp = function(data, fRepower) {
* on the adapter's state, which is why I envisioned a subsequent syncMouse() call. And you would want
* to do that as a separate call, not as part of attachMouse(), because componentAdapter isn't
* set until attachMouse() returns.
*
*
* However, syncMouse() seems unnecessary, given that SerialPort initializes its MCR to an "inactive"
* state, and even when restoring a previous state, if we've done our job properly, both SerialPort and Mouse
* should be restored in sync, making any explicit attempt at sync'ing unnecessary (or so I hope).
* should be restored in sync, making any explicit attempt at sync'ing unnecessary (or so I hope).
*/
// this.componentAdapter.syncMouse();
break;
@ -242,7 +242,7 @@ Mouse.prototype.powerUp = function(data, fRepower) {
/**
* powerDown(fSave)
*
*
* @this {Mouse}
* @param {boolean} fSave
* @return {Object|boolean}
@ -253,7 +253,7 @@ Mouse.prototype.powerDown = function(fSave) {
/**
* reset()
*
*
* @this {Mouse}
*/
Mouse.prototype.reset = function() {
@ -264,7 +264,7 @@ Mouse.prototype.reset = function() {
* save()
*
* This implements save support for the Mouse component.
*
*
* @this {Mouse}
* @return {Object}
*/
@ -278,7 +278,7 @@ Mouse.prototype.save = function() {
* restore(data)
*
* This implements restore support for the Mouse component.
*
*
* @this {Mouse}
* @param {Object} data
* @return {boolean} true if successful, false if failure
@ -289,7 +289,7 @@ Mouse.prototype.restore = function(data) {
/**
* initState(data)
*
*
* @this {Mouse}
* @param {Array} [data]
* @return {boolean} true if successful, false if failure
@ -310,7 +310,7 @@ Mouse.prototype.initState = function(data) {
/**
* saveState()
*
*
* @this {Mouse}
* @return {Array}
*/
@ -333,7 +333,7 @@ Mouse.prototype.saveState = function() {
*
* NOTE: addEventListener() wasn't supported in IE until IE9, but that's OK, because IE9 is the
* oldest IE we support anyway (since older versions of IE lacked complete HTML5/canvas support).
*
*
* @this {Mouse}
* @param {Object} control from the HTML DOM (eg, the canvas for the simulated screen)
*/
@ -365,13 +365,13 @@ Mouse.prototype.captureMouse = function(control) {
this.fCaptured = true;
}
/*
* None of these tricks seemed to work for IE10, so I'm giving up hiding the browser's mouse pointer in IE for now.
* None of these tricks seemed to work for IE10, so I'm giving up hiding the browser's mouse pointer in IE for now.
*
* control['style']['cursor'] = "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjbQg61aAAAADUlEQVQYV2P4//8/IwAI/QL/+TZZdwAAAABJRU5ErkJggg=='), url('/versions/images/current/blank.cur'), none";
*
*
* Setting the cursor style to "none" may not be a standard, but it works in Safari, Firefox and Chrome, so that's pretty
* good for a non-standard!
*
*
* TODO: The reference to '/versions/images/current/blank.cur' is also problematic for anyone who might want
* to run this app from a different server, so think about that as well.
*/
@ -385,7 +385,7 @@ Mouse.prototype.captureMouse = function(control) {
* TODO: Use removeEventListener() if fCaptured, to clean up our handlers; since I'm currently using
* anonymous functions, and since I'm not seeing any compelling reason to remove the handlers once they've
* been established, it's less code to leave them in place.
*
*
* @this {Mouse}
* @param {Object} control from the HTML DOM
*/
@ -408,7 +408,7 @@ Mouse.prototype.releaseMouse = function(control) {
* but I don't think they're available in all browsers. screenX and screenY would work as well.
*
* Anyway, all I care about are deltas. For now.
*
*
* @this {Mouse}
* @param {Object} event object from a 'mousemove' event (specifically, a MouseEvent object)
*/
@ -430,7 +430,7 @@ Mouse.prototype.moveMouse = function(event) {
/**
* clickMouse(iButton, fDown)
*
*
* @this {Mouse}
* @param {number} iButton is 0 for fButton1 (the LEFT button), 2 for fButton2 (the RIGHT button)
* @param {boolean} fDown
@ -470,7 +470,7 @@ Mouse.prototype.clickMouse = function(iButton, fDown) {
* Byte 1 X 1 LB RB Y7 Y6 X7 X6
* Byte 2 X 0 X5 X4 X3 X2 X1 X0
* Byte 3 X 0 Y5 Y4 Y3 Y2 Y1 Y0
*
*
* @this {Mouse}
* @param {string|null} [sDiag] diagnostic message
* @param {number} [xDiag] original x-coordinate (optional; for diagnostic use only)
@ -499,7 +499,7 @@ Mouse.prototype.sendPacket = function(sDiag, xDiag, yDiag) {
* sitting in the RBR), and then writes 0x0B to the MCR (DTR on, RTS on). This last step is consistent with making
* the mouse "active", but it is NOT consistent with "toggling DTR", so I conclude that a reset is ALSO sufficient
* for sending the identification byte. Right or wrong, this gets the ball rolling for Windows v1.01.
*
*
* @this {Mouse}
* @param {number} bMCR
*/
@ -532,7 +532,7 @@ Mouse.prototype.notifyMCR = function(bMCR) {
* At the very least, Windows will have (re)masked the serial port's IRQ, so what does it matter? Not much,
* I just would have preferred that fActive properly reflect whether we should continue dispatching mouse
* events, displaying MESSAGE_MOUSE messages, etc.
*
*
* We could ask the ChipSet component to notify the SerialPort component whenever its IRQ is masked/unmasked,
* and then have the SerialPort pass that notification on to us, but I'm assuming that in the real world,
* a mouse device that's still powered may still send event data to the serial port, and if there was software
@ -550,7 +550,7 @@ Mouse.prototype.notifyMCR = function(bMCR) {
* messageDebugger(sMessage)
*
* This is a combination of the Debugger's messageEnabled(MESSAGE_MOUSE) and message() functions, for convenience.
*
*
* @this {Mouse}
* @param {string} sMessage is any caller-defined message string
*/

View file

@ -4,7 +4,7 @@
* @version 1.0
* @suppress {missingProperties}
* Created 2012-Jun-19
*
*
* Copyright © 2012-2014 Jeff Parsons <Jeff@pcjs.org>
*
* This file is part of PCjs, which is part of the JavaScript Machines Project (aka JSMachines)
@ -34,15 +34,15 @@
"use strict";
if (typeof module !== 'undefined') {
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
}
/**
* Panel(parmsPanel)
*
* The Panel component has no required (parmsPanel) properties.
*
*
* @constructor
* @extends Component
* @param {Object} parmsPanel
@ -55,7 +55,7 @@ Component.subclass(Component, Panel);
/**
* setBinding(sHTMLClass, sHTMLType, sBinding, control)
*
*
* The Panel doesn't have any bindings of its own; it passes along all binding requests to
* the Computer, CPU, Keyboard and Debugger components. The order shouldn't matter, since any
* component that doesn't recognize the specified binding should simply ignore it.
@ -95,7 +95,7 @@ Panel.prototype.initBus = function(cmp, bus, cpu, dbg)
/**
* powerUp(data, fRepower)
*
*
* @this {Panel}
* @param {Object|null} data
* @param {boolean} [fRepower]
@ -111,7 +111,7 @@ Panel.prototype.powerUp = function(data, fRepower)
/**
* powerDown(fSave)
*
*
* @this {Panel}
* @param {boolean} fSave
* @return {Object|boolean}
@ -123,19 +123,19 @@ Panel.prototype.powerDown = function(fSave)
/**
* Panel.init()
*
*
* This function operates on every element (e) of class "panel", and initializes
* all the necessary HTML to construct the Panel module(s) as spec'ed.
*
* Note that each element (e) of class "panel" is expected to have a "data-value"
* attribute containing the same JSON-encoded parameters that the Panel constructor
* expects.
*
*
* NOTE: Unlike most other component init() functions, this one is designed to be
* called multiple times: once at load time, so that we can binding our print()
* function to the panel's output control ASAP, and again when the Computer component
* is verifying that all components are ready and invoking their setPower() functions.
*
*
* Our setPower() method gives us a second opportunity to notify any components that
* that might care (eg, CPU, Keyboard, and Debugger) that we have some controls they
* might want to use.

View file

@ -34,9 +34,9 @@
"use strict";
if (typeof module !== 'undefined') {
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var ROM = require("./rom");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var ROM = require("./rom");
}
/**
@ -49,7 +49,7 @@ if (typeof module !== 'undefined') {
*
* NOTE: We make a note of the specified size, but no memory is initially allocated
* for the RAM until the Computer component calls setPower().
*
*
* @constructor
* @extends Component
* @param {Object} parmsRAM
@ -69,7 +69,7 @@ Component.subclass(Component, RAM);
/**
* initBus(cmp, bus, cpu, dbg)
*
*
* @this {RAM}
* @param {Computer} cmp
* @param {Bus} bus
@ -113,7 +113,7 @@ RAM.prototype.powerUp = function(data, fRepower) {
/**
* powerDown(fSave)
*
*
* @this {RAM}
* @param {boolean} fSave
* @return {Object|boolean}
@ -146,7 +146,7 @@ RAM.prototype.powerDown = function(fSave) {
* object was not given a specific size (see fInstalled). If there are other RAM objects in the system,
* they must necessarily specify a non-conflicting, non-zero start address, in which case their sizeRAM
* value will never be affected by the ChipSet settings.
*
*
* @this {RAM}
*/
RAM.prototype.reset = function() {

View file

@ -34,10 +34,10 @@
"use strict";
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var DumpAPI = require("../../shared/lib/dumpapi");
var Component = require("../../shared/lib/component");
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var DumpAPI = require("../../shared/lib/dumpapi");
var Component = require("../../shared/lib/component");
}
/**
@ -56,7 +56,7 @@ if (typeof module !== 'undefined') {
*
* Also, while the size parameter may seem redundant, I consider it useful to confirm that the ROM you received
* is the ROM you expected.
*
*
* @constructor
* @extends Component
* @param {Object} parmsROM
@ -109,11 +109,11 @@ ROM.BIOS.RESET_FLAG_WARMBOOT = 0x1234; // value stored at ROM.BIOS.RESET_FLAG t
/*
* NOTE: There's currently no need for this component to have a reset() function, since
* once the ROM data is loaded, it can't be changed, so there's nothing to reinitialize.
*
*
* OK, well, I take that back, because the Debugger, if installed, has the ability to modify
* ROM contents, so in that case, having a reset() function that restores the original ROM data
* might be useful; then again, it might not, depending on what you're trying to debug.
*
*
* If we do add reset(), then we'll want to change copyROM() to hang onto the original
* ROM data; currently, we release it after copying it into the read-only memory allocated
* via bus.addMemory().
@ -121,7 +121,7 @@ ROM.BIOS.RESET_FLAG_WARMBOOT = 0x1234; // value stored at ROM.BIOS.RESET_FLAG t
/**
* initBus(cmp, bus, cpu, dbg)
*
*
* @this {ROM}
* @param {Computer} cmp
* @param {Bus} bus
@ -166,7 +166,7 @@ ROM.prototype.powerUp = function(data, fRepower)
* this function. But it doesn't hurt anything, and maybe we'll use our state to save something
* useful down the road, like user-defined symbols (ie, symbols that the Debugger may have
* created, above and beyond those symbols we automatically loaded, if any, along with the ROM).
*
*
* @this {ROM}
* @param {boolean} fSave
* @return {Object|boolean}
@ -178,7 +178,7 @@ ROM.prototype.powerDown = function(fSave)
/**
* onLoadROM(sROMFile, sROMData, nErrorCode)
*
*
* @this {ROM}
* @param {string} sROMFile
* @param {string} sROMData
@ -198,7 +198,7 @@ ROM.prototype.onLoadROM = function(sROMFile, sROMData, nErrorCode)
var rom = eval("(" + sROMData + ")");
var ab = rom['bytes'];
var adw = rom['data'];
if (ab) {
this.abROM = ab;
}
@ -304,7 +304,7 @@ ROM.prototype.copyROM = function()
/**
* addROM(addr)
*
*
* If addr is null or undefined, then it's presumably an unused addrROMAlias, which we simply ignore (it's not
* considered a failure condition).
*

View file

@ -34,10 +34,10 @@
"use strict";
if (typeof module !== 'undefined') {
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var ChipSet = require("./chipset");
var State = require("./state");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var ChipSet = require("./chipset");
var State = require("./state");
}
/**
@ -57,7 +57,7 @@ if (typeof module !== 'undefined') {
*
* DOS typically names the Primary adapter "COM1" and the Secondary adapter "COM2", but I prefer
* to stick to adapter numbers, since not all operating systems follow those naming conventions.
*
*
* @constructor
* @extends Component
* @param {Object} parmsSerial
@ -82,7 +82,7 @@ function SerialPort(parmsSerial) {
/**
* controlIOBuffer is a DOM element, if any, bound to the port (currently for output purposes only; see echoByte())
*
*
* @type {Object}
*/
this.controlIOBuffer = null;
@ -98,12 +98,12 @@ function SerialPort(parmsSerial) {
* property {number} portBase
* property {number} nIRQ
* property {Object} controlIOBuffer is a DOM element, if any, bound to the port (for rudimentary output; see echoByte())
*
*
* NOTE: This class declaration started as a way of informing the code inspector of the controlIOBuffer property,
* which remained undefined until a setBinding() call set it later, but I've since decided that explicitly
* initializing such properties in the constructor is a better way to go -- even though it's more code -- because
* JavaScript compilers are supposed to be happier when the underlying object structures aren't constantly changing.
*
*
* Besides, I'm not sure I want to get into documenting every property this way, for this or any/every other class,
* let alone getting into which ones should be considered private or protected, because PCjs isn't really a library
* for third-party apps.
@ -113,10 +113,10 @@ Component.subclass(Component, SerialPort);
/*
* Internal name used for the I/O buffer control, if any, that we bind to the SerialPort.
*
*
* Alternatively, if SerialPort wants to use another component's control (eg, the Panel's
* "print" control), it can specify the name of that control with the 'binding' property.
*
*
* For that binding to succeed, we also need to know the target component; for now, that's
* been hard-coded to "Panel", in part because that's one of the few components we can rely
* upon initializing before we do, but it would be a simple matter to include a component type
@ -126,11 +126,11 @@ SerialPort.sIOBuffer = "buffer";
/*
* 8250 I/O register offsets (add these to a I/O base address to obtain an I/O port address)
*
*
* NOTE: DLL.REG and DLM.REG form a 16-bit divisor into a clock input frequency of 1.8432Mhz. The following
* values should be used for the corresponding baud rates. Rates above 9600 are discouraged by the IBM Tech Ref,
* but rates as high as 128000 are listed on the NS8250A data sheet.
*
*
* Divisor Rate Percent Error
* 0x0900 50
* 0x0600 75
@ -142,7 +142,7 @@ SerialPort.sIOBuffer = "buffer";
* 0x0060 1200
* 0x0040 1800
* 0x003A 2000 0.69%
* 0x0030 2400
* 0x0030 2400
* 0x0020 3600
* 0x0018 4800
* 0x0010 7200
@ -173,7 +173,7 @@ SerialPort.IER.UNUSED = 0xF0; // always zero
/*
* Interrupt ID Register (IIR.REG, offset 2)
*
*
* All interrupt conditions cleared by reading the corresponding register (or, in the case of IRR_INT_THR, writing a new value to THR.REG)
*/
SerialPort.IIR = {};
@ -216,7 +216,7 @@ SerialPort.MCR.UNUSED = 0xE0; // always zero
/*
* Line Status Register (LSR.REG, offset 5)
*
*
* NOTE: I've seen different specs for the LSR_TSRE. I'm following the IBM Tech Ref's lead here, but the data sheet I have calls it TEMT
* instead of TSRE, and claims that it is set whenever BOTH the THR and TSR are empty, and clear whenever EITHER the THR or TSR contain data.
*/
@ -247,7 +247,7 @@ SerialPort.MSR.RLSD = 0x80; // complement of the RLSD (Received Line
/**
* attachMouse(id, mouse)
*
*
* @this {SerialPort}
* @param {string} id
* @param {Mouse} mouse component
@ -265,7 +265,7 @@ SerialPort.prototype.attachMouse = function(id, mouse) {
* syncMouse()
*
* NOTE: This is probably obsolete, but the Mouse component still might discover a need for it. See Mouse.powerUp().
*
*
* @this {SerialPort}
*
SerialPort.prototype.syncMouse = function() {
@ -275,7 +275,7 @@ SerialPort.prototype.syncMouse = function() {
/**
* setBinding(sHTMLClass, sHTMLType, sBinding, control)
*
*
* @this {SerialPort}
* @param {string|null} sHTMLClass is the class of the HTML control (eg, "input", "output")
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
@ -291,7 +291,7 @@ SerialPort.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, cont
/*
* 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).
*
*
* WARNING: This isn't really a supported feature yet; very much a work-in-progress.
*/
control.onkeydown = function onKeyDownSerial(event) {
@ -316,7 +316,7 @@ SerialPort.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, cont
serial.sendRBR([charCode]);
};
return true;
default:
break;
}
@ -325,7 +325,7 @@ SerialPort.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, cont
/**
* initBus(cmp, bus, cpu, dbg)
*
*
* @this {SerialPort}
* @param {Computer} cmp
* @param {Bus} bus
@ -347,7 +347,7 @@ SerialPort.prototype.initBus = function(cmp, bus, cpu, dbg) {
/**
* powerUp(data, fRepower)
*
*
* @this {SerialPort}
* @param {Object|null} data
* @param {boolean} [fRepower]
@ -366,7 +366,7 @@ SerialPort.prototype.powerUp = function(data, fRepower) {
/**
* powerDown(fSave)
*
*
* @this {SerialPort}
* @param {boolean} fSave
* @return {Object|boolean}
@ -377,7 +377,7 @@ SerialPort.prototype.powerDown = function(fSave) {
/**
* reset()
*
*
* @this {SerialPort}
*/
SerialPort.prototype.reset = function() {
@ -388,7 +388,7 @@ SerialPort.prototype.reset = function() {
* save()
*
* This implements save support for the SerialPort component.
*
*
* @this {SerialPort}
* @return {Object}
*/
@ -402,7 +402,7 @@ SerialPort.prototype.save = function() {
* restore(data)
*
* This implements restore support for the SerialPort component.
*
*
* @this {SerialPort}
* @param {Object} data
* @return {boolean} true if successful, false if failure
@ -413,7 +413,7 @@ SerialPort.prototype.restore = function(data) {
/**
* initState(data)
*
*
* @this {SerialPort}
* @param {Array} [data]
* @return {boolean} true if successful, false if failure
@ -454,7 +454,7 @@ SerialPort.prototype.initState = function(data) {
/**
* saveRegisters()
*
*
* @this {SerialPort}
* @return {Array}
*/
@ -476,7 +476,7 @@ SerialPort.prototype.saveRegisters = function() {
/**
* sendRBR(ab)
*
*
* @this {SerialPort}
* @param {Array} ab is an array of bytes to propagate to the bRBR (Receiver Buffer Register)
*/
@ -487,7 +487,7 @@ SerialPort.prototype.sendRBR = function(ab) {
/**
* advanceRBR()
*
*
* @this {SerialPort}
*/
SerialPort.prototype.advanceRBR = function() {
@ -500,7 +500,7 @@ SerialPort.prototype.advanceRBR = function() {
/**
* inRBR(port, addrFrom)
*
*
* @this {SerialPort}
* @param {number} port (0x3F8 or 0x2F8)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
@ -516,7 +516,7 @@ SerialPort.prototype.inRBR = function(port, addrFrom) {
/**
* inIER(port, addrFrom)
*
*
* @this {SerialPort}
* @param {number} port (0x3F9 or 0x2F9)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
@ -530,7 +530,7 @@ SerialPort.prototype.inIER = function(port, addrFrom) {
/**
* inIIR(port, addrFrom)
*
*
* @this {SerialPort}
* @param {number} port (0x3FA or 0x2FA)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
@ -544,7 +544,7 @@ SerialPort.prototype.inIIR = function(port, addrFrom) {
/**
* inLCR(port, addrFrom)
*
*
* @this {SerialPort}
* @param {number} port (0x3FB or 0x2FB)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
@ -558,7 +558,7 @@ SerialPort.prototype.inLCR = function(port, addrFrom) {
/**
* inMCR(port, addrFrom)
*
*
* @this {SerialPort}
* @param {number} port (0x3FC or 0x2FC)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
@ -572,7 +572,7 @@ SerialPort.prototype.inMCR = function(port, addrFrom) {
/**
* inLSR(port, addrFrom)
*
*
* @this {SerialPort}
* @param {number} port (0x3FD or 0x2FD)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
@ -586,7 +586,7 @@ SerialPort.prototype.inLSR = function(port, addrFrom) {
/**
* inMSR(port, addrFrom)
*
*
* @this {SerialPort}
* @param {number} port (0x3FE or 0x2FE)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
@ -600,7 +600,7 @@ SerialPort.prototype.inMSR = function(port, addrFrom) {
/**
* outTHR(port, bOut, addrFrom)
*
*
* @this {SerialPort}
* @param {number} port (0x3F8 or 0x2F8)
* @param {number} bOut
@ -624,7 +624,7 @@ SerialPort.prototype.outTHR = function(port, bOut, addrFrom) {
/**
* outIER(port, bOut, addrFrom)
*
*
* @this {SerialPort}
* @param {number} port (0x3F9 or 0x2F9)
* @param {number} bOut
@ -641,7 +641,7 @@ SerialPort.prototype.outIER = function(port, bOut, addrFrom) {
/**
* outLCR(port, bOut, addrFrom)
*
*
* @this {SerialPort}
* @param {number} port (0x3FB or 0x2FB)
* @param {number} bOut
@ -654,7 +654,7 @@ SerialPort.prototype.outLCR = function(port, bOut, addrFrom) {
/**
* outMCR(port, bOut, addrFrom)
*
*
* @this {SerialPort}
* @param {number} port (0x3FC or 0x2FC)
* @param {number} bOut
@ -671,7 +671,7 @@ SerialPort.prototype.outMCR = function(port, bOut, addrFrom) {
/**
* updateIRR()
*
*
* @this {SerialPort}
*/
SerialPort.prototype.updateIRR = function() {
@ -691,7 +691,7 @@ SerialPort.prototype.updateIRR = function() {
/**
* echoByte(b)
*
*
* @this {SerialPort}
* @param {number} b
* @return {boolean} true if echoed, false if not
@ -715,7 +715,7 @@ SerialPort.prototype.echoByte = function(b) {
* messageDebugger(sMessage)
*
* This is a combination of the Debugger's messageEnabled(MESSAGE_SERIAL) and message() functions, for convenience.
*
*
* @this {SerialPort}
* @param {string} sMessage is any caller-defined message string
*/
@ -731,7 +731,7 @@ SerialPort.prototype.messageDebugger = function(sMessage) {
* messagePort(port, bOut, addrFrom, name, bIn)
*
* This is an internal version of the Debugger's messagePort() function, for convenience.
*
*
* @this {SerialPort}
* @param {number} port
* @param {number|null} bOut if an output operation

File diff suppressed because it is too large Load diff

View file

@ -34,19 +34,19 @@
"use strict";
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var Bus = require("./bus");
var State = require("./state");
var CPU = require("./cpu");
var X86 = require("./x86");
var X86Seg = require("./x86seg");
var X86Grps = require("./x86grps");
var X86Help = require("./x86help");
var X86Mods = require("./x86mods");
var X86OpXX = require("./x86opxx");
var X86Op0F = require("./x86op0f");
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var Bus = require("./bus");
var State = require("./state");
var CPU = require("./cpu");
var X86 = require("./x86");
var X86Seg = require("./x86seg");
var X86Grps = require("./x86grps");
var X86Help = require("./x86help");
var X86Mods = require("./x86mods");
var X86OpXX = require("./x86opxx");
var X86Op0F = require("./x86op0f");
}
/**

View file

@ -34,9 +34,9 @@
"use strict";
if (typeof module !== 'undefined') {
var X86 = require("./x86");
var X86Help = require("./x86help");
var Debugger = require("./debugger");
var X86 = require("./x86");
var X86Help = require("./x86help");
var Debugger = require("./debugger");
}
var X86Grps = {
@ -66,7 +66,7 @@ var X86Grps = {
/**
* NOTE: Notice that some of the simpler math functions could get away with updating resultSize before
* the calculation, but here the calculation depends on the incoming carry value.
*
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
@ -82,7 +82,7 @@ var X86Grps = {
/**
* NOTE: Notice that some of the simpler math functions could get away with updating resultSize before
* the calculation, but here the calculation depends on the incoming carry value.
*
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
@ -169,7 +169,7 @@ var X86Grps = {
/**
* NOTE: Notice that some of the simpler math functions could get away with updating resultSize before
* the calculation, but here the calculation depends on the incoming carry value.
*
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
@ -185,7 +185,7 @@ var X86Grps = {
/**
* NOTE: Notice that some of the simpler math functions could get away with updating resultSize before
* the calculation, but here the calculation depends on the incoming carry value.
*
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
@ -296,7 +296,7 @@ var X86Grps = {
* operand size of the last operation. And since only 2 of the 6 arithmetic flags need to change, that tips the scales
* in favor of leaving resultSize alone. However, the previous code that worked so hard to update resultSize is still
* here, commented out; it works, but it's less efficient.
*
*
* @this {X86CPU}
* @param {number} result (untruncated, so that we can inspect it for CARRY and OVERFLOW)
* @param {number} size
@ -497,7 +497,7 @@ var X86Grps = {
* and/or code that depends on them, I'll continue setting PS_AF and PS_OF "normally".
*
* See also: AND, OR, TEST, and XOR (those instructions leave AUXCARRY "undefined" as well).
*
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src (1 or CL, or an immediate byte for 80186/80188 and up)
@ -522,7 +522,7 @@ var X86Grps = {
* AUXCARRY (PS_AF), AUXCARRY isn't properly set on a real 8086/8088; its value is
* documented as "undefined." Similarly, OVERFLOW (PS_OF) is documented as "undefined"
* for shifts > 1. See opGrpSHLb() for more details.
*
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src (1 or CL, or an immediate byte for 80186/80188 and up)
@ -547,7 +547,7 @@ var X86Grps = {
* AUXCARRY (PS_AF), AUXCARRY isn't properly set on a real 8086/8088; its value is
* documented as "undefined." Similarly, OVERFLOW (PS_OF) is documented as "undefined"
* for shifts > 1. See opGrpSHLb() for more details.
*
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src (1 or CL, or an immediate byte for 80186/80188 and up)
@ -572,7 +572,7 @@ var X86Grps = {
* AUXCARRY (PS_AF), AUXCARRY isn't properly set on a real 8086/8088; its value is
* documented as "undefined." Similarly, OVERFLOW (PS_OF) is documented as "undefined"
* for shifts > 1. See opGrpSHLb() for more details.
*
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src (1 or CL, or an immediate byte for 80186/80188 and up)
@ -597,7 +597,7 @@ var X86Grps = {
* AUXCARRY (PS_AF), AUXCARRY isn't properly set on a real 8086/8088; its value is
* documented as "undefined." Similarly, OVERFLOW (PS_OF) is documented as "undefined"
* for shifts > 1. See opGrpSHLb() for more details.
*
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src (1 or CL, or an immediate byte for 80186/80188 and up)
@ -623,7 +623,7 @@ var X86Grps = {
* AUXCARRY (PS_AF), AUXCARRY isn't properly set on a real 8086/8088; its value is
* documented as "undefined." Similarly, OVERFLOW (PS_OF) is documented as "undefined"
* for shifts > 1. See opGrpSHLb() for more details.
*
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src (1 or CL, or an immediate byte for 80186/80188 and up)
@ -739,7 +739,7 @@ var X86Grps = {
}
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
@ -783,7 +783,7 @@ var X86Grps = {
}
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
@ -822,7 +822,7 @@ var X86Grps = {
this.resultSize = X86.RESULT.SIZE_BYTE;
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
@ -836,7 +836,7 @@ var X86Grps = {
* @param {number} dst
* @param {number} src (null)
* @return {number} (we return dst unchanged, since it's actually AX that's modified)
*
*
* TODO: Implement the following difference, from "AP-186: Introduction to the 80186 Microprocessor, March 1983":
*
* "The 8086 will cause a divide error whenever the absolute value of the quotient is greater then 7FFFH
@ -869,7 +869,7 @@ var X86Grps = {
this.resultSize = X86.RESULT.SIZE_BYTE;
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
@ -900,7 +900,7 @@ var X86Grps = {
}
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
@ -914,15 +914,15 @@ var X86Grps = {
* lower 16 bits (carry clear) and when the upper 16 bits contain significant bits (carry set). The latter
* will occur whenever a positive result is > 32767 (0x00007fff) and whenever a negative result is < -32768
* (0xffff8000).
*
*
* Example 1: 256 * 64 = 16384 (0x00004000): carry is clear
* Example 2: 256 * 128 = 32768 (0x00008000): carry is set (the sign bit no longer fits in the lower 16 bits)
* Example 3: 256 * -128 (0xff80) = -32768 (0xffff8000): carry is clear (the sign bit *still* fits in the lower 16 bits)
* Example 4: 256 * -256 (0xff00) = -65536 (0xffff0000): carry is set (the sign bit no longer fits in the lower 16 bits)
*
*
* An earlier version of this function assumed it simply needed to check bit 15 of the result to determine carry,
* which was completely broken.
*
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src (null)
@ -945,7 +945,7 @@ var X86Grps = {
}
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
@ -970,7 +970,7 @@ var X86Grps = {
}
/*
* Detect small divisor (quotient overflow)
*
*
* WARNING: We CANNOT simply do "src = (this.regDX << 16) | this.regAX", because if bit 15 of DX
* is set, JavaScript will create a negative 32-bit number. So we instead use non-bit-wise operators
* to force JavaScript to create a floating-point value that won't suffer from 32-bit-math side-effects.
@ -990,7 +990,7 @@ var X86Grps = {
this.resultSize = X86.RESULT.SIZE_WORD;
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
@ -1040,7 +1040,7 @@ var X86Grps = {
this.resultSize = X86.RESULT.SIZE_WORD;
/*
* Multiply/divide instructions specify only a single operand, which the decoders pass to us
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* via the dst parameter, so we set src to the other implied operand (either AX or DX:AX).
* However, src is technically an output, and dst is merely an input (which is why we must return
* dst unchanged). So, to make traceLog() more consistent, we reverse the order of dst and src.
*/
@ -1258,7 +1258,7 @@ var X86Grps = {
/*
* A word (or two) on instruction groups (eg, GRP1, GRP2), which are groups of instructions that
* use a mod/reg/rm byte, where the reg field of that byte selects a function rather than a register.
*
*
* I start with the groupings used by Intel's "Pentium Processor User's Manual (Volume 3: Architecture
* and Programming Manual)", but I deviate slightly, mostly by subdividing their groups with the use
* of suffixes:
@ -1275,7 +1275,7 @@ var X86Grps = {
* 0x0F,0x01 Grp7 GRP7 (SGDT, SIDT, LGDT, LIDT, SMSW, LMSW, INVLPG) Group G
* 0x0F,0xBA Grp8 GRP8 (BT, BTS, BTR, BTC) Group H
* 0x0F,0xC7 Grp9 GRP9 (CMPXCH) (N/A, 80386 and up?)
*
*
* My only serious deviation is Grp5, which I refer to as GRP4w, because it contains word forms of
* the INC and DEC instructions found in GRP4b. Granted, GRP4w also contains versions of the CALL,
* JMP and PUSH instructions, which are not in GRP4b, but there's nothing in GRP4b that conflicts with

View file

@ -34,8 +34,8 @@
"use strict";
if (typeof module !== 'undefined') {
var X86 = require("./x86");
var Debugger = require("./debugger");
var X86 = require("./x86");
var Debugger = require("./debugger");
}
var X86Help = {
@ -127,14 +127,14 @@ var X86Help = {
* @param {number} dst
* @param {number} src
* @return {number}
*
*
* 80286_and_80287_Programmers_Reference_Manual_1987.pdf, p.B-44 (p.254) notes that:
*
*
* "The low 16 bits of the product of a 16-bit signed multiply are the same as those of an
* unsigned multiply. The three operand IMUL instruction can be used for unsigned operands as well."
*
*
* However, we still sign-extend the operands before multiplying, making it easier to range-check the result.
*
*
* (80186/80188 and up)
*/
opHelpIMUL16: function(dst, src) {
@ -237,8 +237,8 @@ var X86Help = {
if (wIndex < wLower || wIndex > wUpper) {
/*
* The INT 0x05 handler must be called with CS:IP pointing to the BOUND instruction.
*
* TODO: Determine the cycle impact when a BOUND exception is triggered, over and above nOpCyclesBound.
*
* TODO: Determine the cycle impact when a BOUND exception is triggered, over and above nOpCyclesBound.
*/
this.setIP(this.opEA - this.segCS.base);
X86Help.opHelpINT.call(this, X86.EXCEPTION.BOUND_ERR, null, 0);
@ -273,7 +273,7 @@ var X86Help = {
/*
* Currently, segVER.load() will return an error only if the selector is beyond the bounds of the
* descriptor table or the descriptor is not for a segment.
*
*
* TODO: This instruction's 80286 documentation does not discuss conforming code segments; determine
* if we need a special check for them.
*/
@ -297,7 +297,7 @@ var X86Help = {
/*
* Currently, segVER.load() will return an error only if the selector is beyond the bounds of the
* descriptor table or the descriptor is not for a segment.
*
*
* TODO: LSL is explicitly documented as ALSO requiring a non-null selector, so we check X86.SEL.MASK;
* are there any other instructions that were, um, less explicit but also require a non-null selector?
*/

View file

@ -34,7 +34,7 @@
"use strict";
if (typeof module !== 'undefined') {
var X86 = require("./x86");
var X86 = require("./x86");
}
var X86Mods = {
@ -15350,7 +15350,7 @@ var X86Mods = {
* mod=11 (reg:dst) reg=110 (afnGrp[6]) r/m=100 (SP)
*/
opModGrpWordF4: function(afnGrp, fnSrc) {
this.opFlags |= X86.OPFLAG.PUSHSP; // we limit this hack to the only ModRM function that calls opGrpPUSHw() with SP
this.opFlags |= X86.OPFLAG.PUSHSP; // we limit this hack to the only ModRM function that calls opGrpPUSHw() with SP
this.regSP = afnGrp[6].call(this, this.regSP, fnSrc.call(this));
},
/**

View file

@ -34,10 +34,10 @@
"use strict";
if (typeof module !== 'undefined') {
var X86 = require("./x86");
var X86Grps = require("./x86grps");
var X86Help = require("./x86help");
var X86Mods = require("./x86mods");
var X86 = require("./x86");
var X86Grps = require("./x86grps");
var X86Help = require("./x86help");
var X86Mods = require("./x86mods");
}
var X86Op0F = {
@ -149,10 +149,10 @@ var X86Op0F = {
/*
* For VERR, if the code segment is readable and conforming, the descriptor privilege level
* (DPL) can be any value.
*
*
* Otherwise, DPL must be greater than or equal to (have less or the same privilege as) both the
* current privilege level and the selector's RPL.
*
*
* TODO: Consider making a CPL (current privilege level) variable that tracks segCS.sel, so that we
* don't have to mask segCS.sel every time.
*/
@ -187,7 +187,7 @@ var X86Op0F = {
/*
* DPL must be greater than or equal to (have less or the same privilege as) both the current
* privilege level and the selector's RPL.
*
*
* TODO: Consider making a CPL (current privilege level) variable that tracks segCS.sel, so that we
* don't have to mask segCS.sel every time.
*/
@ -261,8 +261,8 @@ var X86Op0F = {
* opLIDT(dst, src)
*
* The 80286 LIDT instruction expects a 40-bit operand: a 16-bit limit, followed by a 24-bit address;
* the ModRM decoder has already supplied the first word of the operand (in dst), which corresponds to the
* limit, so we must fetch the remaining 24 bits ourselves.
* the ModRM decoder has already supplied the first word of the operand (in dst), which corresponds to the
* limit, so we must fetch the remaining 24 bits ourselves.
*
* @this {X86CPU}
* @param {number} dst
@ -273,7 +273,7 @@ var X86Op0F = {
if (this.regEA < 0) {
X86Help.opInvalid.call(this);
} else {
this.addrIDT = this.getWord(this.regEA + 2) | (this.getByte(this.regEA + 4) << 16);
this.addrIDT = this.getWord(this.regEA + 2) | (this.getByte(this.regEA + 4) << 16);
this.addrIDTLimit = this.addrIDT + dst;
if (FASTDISABLE) this.setEAWord = this.setEAWordDisabled; else this.opFlags |= X86.OPFLAG.NOWRITE;
this.nStepCycles -= 12;
@ -389,7 +389,7 @@ X86Op0F.aOps0F = [
* the instructions in GRP6 and GRP7 only read their dst operand (eg, LLDT), which means the ModRM helper function
* must insure that setEAWord() is disabled, while others only write their dst operand (eg, SLDT), which means that
* getEAWord() should be disabled *prior* to calling the ModRM helper function. This latter case requires that
* we decode the reg field of the ModRM byte before dispatching.
* we decode the reg field of the ModRM byte before dispatching.
*/
X86Op0F.aOpGRP6Prot = [
X86Op0F.opSLDT, X86Op0F.opSTR, X86Op0F.opLLDT, X86Op0F.opLTR, // 0x0F,0x00(reg=0x0-0x3)

View file

@ -34,12 +34,12 @@
"use strict";
if (typeof module !== 'undefined') {
var Component = require("../../shared/lib/component");
var X86 = require("./x86");
var X86Grps = require("./x86grps");
var X86Help = require("./x86help");
var X86Mods = require("./x86mods");
var X86Op0F = require("./x86op0f");
var Component = require("../../shared/lib/component");
var X86 = require("./x86");
var X86Grps = require("./x86grps");
var X86Help = require("./x86help");
var X86Mods = require("./x86mods");
var X86Op0F = require("./x86op0f");
}
var X86OpXX = {
@ -1194,14 +1194,14 @@ var X86OpXX = {
opINSb: function() {
var nReps = 1;
var nDelta = 0;
/*
* NOTE: 5 + 4n is the cycle time for the 80286; the 80186/80188 has different values: 14 cycles for
* an unrepeated INS, and 8 + 8n for a repeated INS. However, accurate cycle times for the 80186/80188 is
* low priority.
*/
var nCycles = 5;
/*
* The (normal) REP prefix, if used, is REPNZ (0xf2), but either one works....
*/
@ -1210,7 +1210,7 @@ var X86OpXX = {
nDelta = 1;
if (this.opPrefixes & X86.OPFLAG.REPEAT) nCycles = 4;
}
if (nReps--) {
var b = this.bus.checkPortInputNotify(this.regDX, this.regEIP - nDelta - 1);
this.setSOByte(this.segES, this.regDI, b);
@ -1240,7 +1240,7 @@ var X86OpXX = {
opINSw: function() {
var nReps = 1;
var nDelta = 0;
/*
* NOTE: 5 + 4n is the cycle time for the 80286; the 80186/80188 has different values: 14 cycles for
* an unrepeated INS, and 8 + 8n for a repeated INS. However, accurate cycle times for the 80186/80188 is
@ -1278,13 +1278,13 @@ var X86OpXX = {
* @this {X86CPU}
*
* NOTE: Segment overrides are ignored for this instruction, so we must use segDS instead of segData.
*
*
* op=0x6E (outsb) (80186/80188 and up)
*/
opOUTSb: function() {
var nReps = 1;
var nDelta = 0;
/*
* NOTE: 5 + 4n is the cycle time for the 80286; the 80186/80188 has different values: 14 cycles for
* an unrepeated INS, and 8 + 8n for a repeated INS. However, accurate cycle times for the 80186/80188 is
@ -1327,7 +1327,7 @@ var X86OpXX = {
opOUTSw: function() {
var nReps = 1;
var nDelta = 0;
/*
* NOTE: 5 + 4n is the cycle time for the 80286; the 80186/80188 has different values: 14 cycles for
* an unrepeated INS, and 8 + 8n for a repeated INS. However, accurate cycle times for the 80186/80188 is
@ -1647,15 +1647,15 @@ var X86OpXX = {
* If the second operand is a register, then the ModeRegByte decoder must use separate "get" and
* "set" assignments, otherwise instructions like "XCHG DH,DL" will end up using a stale DL instead of
* our updated DL.
*
*
* To be clear, a single assignment like this will fail:
*
*
* opModeRegByteF2: function(fn) {
* this.regDX = (this.regDX & 0xff) | (fn.call(this, this.regDX >> 8, this.regDX & 0xff) << 8);
* }
*
* which is why all affected decoders now use separate assignments; eg:
*
*
* opModeRegByteF2: function(fn) {
* var b = fn.call(this, this.regDX >> 8, this.regDX & 0xff);
* this.regDX = (this.regDX & 0xff) | (b << 8);
@ -1981,7 +1981,7 @@ var X86OpXX = {
},
/**
* @this {X86CPU}
*
*
* op=0x9B (wait)
*/
opWAIT: function() {
@ -2019,7 +2019,7 @@ var X86OpXX = {
opSAHF: function() {
/*
* NOTE: While it make LOOK more efficient to do this:
*
*
* this.setPS((this.getPS() & ~X86.PS.SAHF) | ((this.regAX >> 8) & X86.PS.SAHF));
*
* the call to getPS() forces all the "indirect" flags to be resolved first, and then the call
@ -2729,7 +2729,7 @@ var X86OpXX = {
* @this {X86CPU}
*
* Here's the pseudo-code from http://www.pcjs.org/pubs/pc/reference/intel/80286/progref, p.B-40 (p.250):
*
*
* LEVEL := LEVEL MOD 32
* Push BP
* Set a temporary value FRAME_PTR := SP
@ -2742,7 +2742,7 @@ var X86OpXX = {
* End if
* BP := FRAME_PTR
* SP := SP - first operand
*
*
* TODO: Verify that this pseudo-code is identical on the 80186/80188 (eg, is LEVEL MOD 32 performed in both instances?)
*
* op=0xC8 (enter imm16,imm8) (80186/80188 and up)
@ -2771,7 +2771,7 @@ var X86OpXX = {
},
/**
* @this {X86CPU}
*
*
* Set SP to BP, then pop BP
*
* op=0xC9 (leave) (80186/80188 and up)
@ -3359,7 +3359,7 @@ var X86OpXX = {
/*
* This 256-entry array of opcode functions is at the heart of the CPU engine: stepCPU(n).
*
*
* It might be worth trying a switch() statement instead, to see how the performance compares,
* but I suspect that would vary quite a bit across JavaScript engines; for now, I'm putting my
* money on array lookup.
@ -3405,7 +3405,7 @@ X86OpXX.aOps = [
/*
* On all processors, opcode groups 0x80 and 0x82 perform identically (0x82 opcodes sign-extend their
* immediate data, but since both 0x80 and 0x82 are byte operations, the sign extension has no effect).
*
*
* WARNING: Intel's "Pentium Processor User's Manual (Volume 3: Architecture and Programming Manual)" refers
* to opcode 0x82 as a "reserved" instruction, but also cryptically refers to it as "MOVB AL,imm". This is
* assumed to be an error in the manual, because as far as I know, 0x82 has always mirrored 0x80.

View file

@ -34,17 +34,17 @@
"use strict";
if (typeof module !== 'undefined') {
var X86 = require("./x86");
var X86Help = require("./x86help");
var X86 = require("./x86");
var X86Help = require("./x86help");
}
/**
* X86Seg(cpu, sName)
*
*
* @constructor
* @param {X86CPU} cpu
* @param {string} [sName] segment name
* @param {boolean} [fProt] true if segment register used exclusively in protected-mode
* @param {boolean} [fProt] true if segment register used exclusively in protected-mode
*/
function X86Seg(cpu, sName, fProt)
{
@ -64,7 +64,7 @@ function X86Seg(cpu, sName, fProt)
/**
* loadReal(sel, fSuppress)
*
*
* This is the default real-mode load() function.
*
* @this {X86Seg}
@ -84,7 +84,7 @@ X86Seg.loadReal = function loadReal(sel, fSuppress)
*
* This replaces the segment's default load() function whenever the segment is notified (eg, by the CPU's setProtMode()
* function) the processor is now in protected-mode.
*
*
* Segments in protected-mode are referenced by selectors, which are indexes into descriptor tables (GDT, LDT, IDT) whose
* descriptors are 4-word (8-byte) entries:
*
@ -92,7 +92,7 @@ X86Seg.loadReal = function loadReal(sel, fSuppress)
* word 1: base address low
* word 2: base address high (0-7), segment type (8-11), descriptor type (12), DPL (13-14), present bit (15)
* word 3: used only on 80386 and up (should be set to zero for upward compatibility)
*
*
* See X86.DESC for offset and bit definitions.
*
* @this {X86Seg}
@ -115,23 +115,23 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
if (offDT + 7 <= addrDTLimit) {
this.checkRead = X86Seg.checkReadProtEnabled;
this.checkWrite = X86Seg.checkWriteProtEnabled;
/*
* TODO: This is only the first of many steps toward accurately counting cycles in protected mode;
* I simply noted that "POP segreg" takes 5 cycles in real mode and 20 in protected mode, so I'm
* starting with a 15-cycle difference. Obviously the difference will be much greater when the load fails.
*/
this.cpu.nStepCycles -= 15;
/*
* TODO: Use (direct) Bus memory interfaces instead of (indirect) CPU memory interfaces here?
*/
var limit = this.cpu.getWord(offDT + X86.DESC.LIMIT.OFFSET);
var acc = this.cpu.getWord(offDT + X86.DESC.ACC.OFFSET);
var base = this.cpu.getWord(offDT + X86.DESC.BASE.OFFSET) | ((acc & X86.DESC.ACC.BASE1623) << 16);
Component.assert(this.cpu.getWord(offDT + 0x06) == 0);
/*
* For LSL (which uses fSuppress), we must support X86.DESC.ACC.TYPE.SEG as well as TSS and LDT.
*/
@ -271,7 +271,7 @@ X86Seg.checkWriteProtDisabled = function checkWriteProtDisabled(off, cb, fSuppre
*
* Early versions of PCjs saved only segment selectors, since that's all that mattered in real-mode;
* newer versions need to save/restore the entire segment object.
*
*
* @this {X86Seg}
* @return {Array}
*/
@ -285,7 +285,7 @@ X86Seg.prototype.save = function()
*
* Early versions of PCjs saved only segment selectors, since that's all that mattered in real-mode;
* newer versions need to save/restore the entire segment object.
*
*
* @this {X86Seg}
* @param {Array|number} a
*/
@ -305,12 +305,12 @@ X86Seg.prototype.restore = function(a)
/**
* setBase(addr)
*
*
* This is used in unusual situations where the base must be set independently; normally, the base
* is set according to the selector provided to load(), but there are a few cases where setBase() is
* required (eg, in resetRegs() where the 80286 wants the real-mode CS selector to be 0xF000 but the
* CS base must be 0xFF0000, and LOADALL).
*
*
* @this {X86Seg}
* @param {number} addr
*/