Improved separation of FDC media parameters from drive parameters
This commit is contained in:
parent
2ce025aa78
commit
1809543577
32 changed files with 1915 additions and 1887 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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, it’ll 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;
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
},
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue