Enabled support for local disk images to PCjs
This commit is contained in:
parent
43860a588c
commit
a54cbeeba8
18 changed files with 961 additions and 845 deletions
|
|
@ -128,15 +128,15 @@ C1PSerialPort.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, c
|
|||
};
|
||||
return true;
|
||||
|
||||
case "uploadSerial":
|
||||
case "mountSerial":
|
||||
/*
|
||||
* Check for availability of FileReader
|
||||
*/
|
||||
if (window.FileReader && window.File && window.FileList && window.Blob) {
|
||||
if (window.FileReader && window.File && window.FileList) {
|
||||
this.bindings[sBinding] = control;
|
||||
|
||||
/*
|
||||
* Enable "Load Local File" button only if a file is actually selected
|
||||
* Enable "Mount" button only if a file is actually selected
|
||||
*/
|
||||
control.addEventListener('change', function() {
|
||||
var fieldset = control.children[0];
|
||||
|
|
@ -150,7 +150,7 @@ C1PSerialPort.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, c
|
|||
|
||||
var reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
// serial.println("loading " + file.name + "...");
|
||||
// serial.println("mounting " + file.name + "...");
|
||||
serial.loadFile(file.name, reader.result.toString(), 0);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
|
|
@ -162,7 +162,7 @@ C1PSerialPort.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, c
|
|||
};
|
||||
}
|
||||
else {
|
||||
this.println("FileReader support not available, disabling local file load");
|
||||
if (DEBUG) this.log("FileReader support not available, Mount disabled");
|
||||
control.parentNode.removeChild(control);
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,11 @@
|
|||
font-family: Monaco, monospace;
|
||||
font-size: x-small;
|
||||
}
|
||||
.c1pjs-fieldset {
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.c1pjs-flag {
|
||||
font-family: "Lucida Console", monospace;
|
||||
font-size: small;
|
||||
|
|
|
|||
|
|
@ -286,10 +286,10 @@
|
|||
<div><xsl:value-of select="."/></div>
|
||||
</xsl:when>
|
||||
<xsl:when test="@type = 'file'">
|
||||
<form class="{$APPCLASS}-{@class}" data-value="{$type},{$binding}">
|
||||
<fieldset>
|
||||
<form class="{$APPCLASS}-{@class}" style="{$border}{$width}{$height}{$style}" data-value="{$type},{$binding}">
|
||||
<fieldset class="{$APPCLASS}-fieldset">
|
||||
<input type="file"/>
|
||||
<input type="submit" value="Load Local File" disabled="true"/>
|
||||
<input type="submit" value="Mount" disabled="true"/>
|
||||
</fieldset>
|
||||
</form>
|
||||
</xsl:when>
|
||||
|
|
|
|||
|
|
@ -262,14 +262,6 @@ Computer.sAppName = APPNAME || "PCjs";
|
|||
Computer.sAppVer = APPVERSION;
|
||||
Computer.sCopyright = "Copyright © 2012-2014 Jeff Parsons <Jeff@pcjs.org>";
|
||||
|
||||
/*
|
||||
* Version 1.02 marks the addition of "disk history" to the FDC, so that as diskettes are mounted,
|
||||
* modified, unmounted, and then later remounted, all previous modifications can be restored. This
|
||||
* same disk history has also been added to the save/restore data as of v1.02, so any save/restore
|
||||
* code sensitive to that change must include a reference to VERSION_102.
|
||||
*/
|
||||
Computer.VERSION_102 = 102;
|
||||
|
||||
/*
|
||||
* I think it's a good idea to also display a GPL notice, putting people on notice that even
|
||||
* the "compiled" source code has all the same GPL requirements as the uncompiled source code.
|
||||
|
|
|
|||
|
|
@ -243,23 +243,18 @@ function Disk(controller, drive, mode)
|
|||
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
|
||||
*/
|
||||
this.sDiskName = drive.name;
|
||||
this.nCylinders = drive.nCylinders;
|
||||
this.nHeads = drive.nHeads;
|
||||
this.nSectors = drive.nSectors;
|
||||
this.cbSector = drive.cbSector;
|
||||
this.fRemovable = drive.fRemovable;
|
||||
this.fOnDemand = this.fRemote = false;
|
||||
|
||||
/*
|
||||
* Initialize the disk contents
|
||||
*/
|
||||
this.create();
|
||||
this.create(mode, drive.nCylinders, drive.nHeads, drive.nSectors, drive.cbSector);
|
||||
|
||||
/*
|
||||
* The following dirty sector and timer properties are used only with fOnDemand disks,
|
||||
|
|
@ -428,10 +423,21 @@ Disk.prototype.powerDown = function(fSave, fShutdown)
|
|||
/**
|
||||
* create()
|
||||
*
|
||||
* @param {string} mode
|
||||
* @param {number} nCylinders
|
||||
* @param {number} nHeads
|
||||
* @param {number} nSectors
|
||||
* @param {number} cbSector
|
||||
*
|
||||
* Initializes the disk contents according to the current drive mode and parameters.
|
||||
*/
|
||||
Disk.prototype.create = function()
|
||||
Disk.prototype.create = function(mode, nCylinders, nHeads, nSectors, cbSector)
|
||||
{
|
||||
this.mode = mode;
|
||||
this.nCylinders = nCylinders;
|
||||
this.nHeads = nHeads;
|
||||
this.nSectors = nSectors;
|
||||
this.cbSector = cbSector;
|
||||
this.aDiskData = [];
|
||||
/*
|
||||
* If the drive is using PRELOAD mode, then it will use the load()/mount() process to initialize the disk contents;
|
||||
|
|
@ -462,7 +468,7 @@ Disk.prototype.create = function()
|
|||
}
|
||||
this.aDiskData = aCylinders;
|
||||
}
|
||||
this.dwChecksum = 0;
|
||||
this.dwChecksum = null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -577,42 +583,43 @@ Disk.prototype.load = function(sDiskName, sDiskPath, file, fnNotify, controller)
|
|||
|
||||
/**
|
||||
*
|
||||
* build(buffer, fDirty)
|
||||
* build(buffer, fModified)
|
||||
*
|
||||
* Builds a disk image from an ArrayBuffer (eg, from a FileReader object), rather than from JSON-encoded data.
|
||||
*
|
||||
* @this {Disk}
|
||||
* @param {?} buffer (we KNOW this is an ArrayBuffer, but we can't seem to convince the Closure Compiler)
|
||||
* @param {boolean} [fDirty] is true if we should mark the entire disk dirty (to ensure that we save/restore it)
|
||||
* @param {boolean} [fModified] is true if we should mark the entire disk modified (to ensure that we save/restore it)
|
||||
*/
|
||||
Disk.prototype.build = function(buffer, fDirty)
|
||||
Disk.prototype.build = function(buffer, fModified)
|
||||
{
|
||||
var disk;
|
||||
var cbDiskData = buffer? buffer.byteLength : 0;
|
||||
var disketteFormat = DiskAPI.DISKETTE_FORMATS[cbDiskData];
|
||||
|
||||
if (disketteFormat) {
|
||||
this.nCylinders = disketteFormat[0];
|
||||
this.nHeads = disketteFormat[1];
|
||||
this.nSectors = disketteFormat[2];
|
||||
this.cbSector = 512;
|
||||
|
||||
var cdw = this.cbSector >> 2, dwPattern = 0, dwChecksum = 0;
|
||||
var ib = 0;
|
||||
var dwChecksum = 0;
|
||||
var cbSector = 512, dwPattern = 0;
|
||||
var dv = new DataView(buffer, 0, cbDiskData);
|
||||
this.aDiskData = new Array(disketteFormat[0]);
|
||||
|
||||
this.aDiskData = new Array(this.nCylinders);
|
||||
for (var iCylinder = 0; iCylinder < this.aDiskData.length; iCylinder++) {
|
||||
var cylinder = this.aDiskData[iCylinder] = new Array(disketteFormat[1]);
|
||||
var cylinder = this.aDiskData[iCylinder] = new Array(this.nHeads);
|
||||
for (var iHead = 0; iHead < cylinder.length; iHead++) {
|
||||
var head = cylinder[iHead] = new Array(disketteFormat[2]);
|
||||
var head = cylinder[iHead] = new Array(this.nSectors);
|
||||
for (var iSector = 0; iSector < head.length; iSector++) {
|
||||
var sector = this.initSector(null, iCylinder, iHead, iSector + 1, cbSector, dwPattern);
|
||||
var cdw = cbSector >> 2;
|
||||
var sector = this.initSector(null, iCylinder, iHead, iSector + 1, this.cbSector, dwPattern);
|
||||
var adw = sector['data'];
|
||||
for (var idw = 0; idw < cdw; idw++, ib += 4) {
|
||||
var dw = adw[idw] = dv.getInt32(ib, true);
|
||||
dwChecksum = (dwChecksum + dw) & 0xffffffff;
|
||||
}
|
||||
if (fDirty) {
|
||||
sector.cModify = cdw;
|
||||
sector.fDirty = true;
|
||||
}
|
||||
if (fModified) sector.cModify = cdw;
|
||||
head[iSector] = sector;
|
||||
}
|
||||
}
|
||||
|
|
@ -772,11 +779,17 @@ Disk.prototype.doneLoad = function(sDiskFile, sDiskData, nErrorCode, sDiskPath)
|
|||
* This includes detecting sector data in older formats (eg, the OLD array of 'bytes' instead
|
||||
* of the NEW 'data' array of dwords) and converting them on-the-fly to the current format.
|
||||
*/
|
||||
this.nCylinders = aDiskData.length;
|
||||
this.nHeads = aDiskData[0].length;
|
||||
this.nSectors = aDiskData[0][0].length;
|
||||
var sector = aDiskData[0][0][0];
|
||||
this.cbSector = (sector && sector['length']) || 512;
|
||||
|
||||
var dwChecksum = 0;
|
||||
for (var iCylinder = 0; iCylinder < aDiskData.length; iCylinder++) {
|
||||
for (var iHead = 0; iHead < aDiskData[iCylinder].length; iHead++) {
|
||||
for (var iSector = 0; iSector < aDiskData[iCylinder][iHead].length; iSector++) {
|
||||
var sector = aDiskData[iCylinder][iHead][iSector];
|
||||
for (var iCylinder = 0; iCylinder < this.nCylinders; iCylinder++) {
|
||||
for (var iHead = 0; iHead < this.nHeads; iHead++) {
|
||||
for (var iSector = 0; iSector < this.nSectors; iSector++) {
|
||||
sector = aDiskData[iCylinder][iHead][iSector];
|
||||
if (!sector) continue; // non-standard (eg, XDF) disk images may have "unused" (null) sectors
|
||||
var length = sector['length'];
|
||||
if (length === undefined) { // provide backward-compatibility with older JSON...
|
||||
|
|
@ -1443,7 +1456,7 @@ Disk.prototype.write = function(sector, ibSector, b)
|
|||
*
|
||||
* The first array entry contains some disk information:
|
||||
*
|
||||
* [sDiskPath, dwChecksum]
|
||||
* [sDiskPath, dwChecksum, nCylinders, nHeads, nSectors, cbSector]
|
||||
*
|
||||
* Each subsequent entry in the returned array contains the following:
|
||||
*
|
||||
|
|
@ -1458,7 +1471,7 @@ Disk.prototype.save = function()
|
|||
{
|
||||
var i = 0;
|
||||
var deltas = [];
|
||||
deltas[i++] = [this.sDiskPath, this.dwChecksum];
|
||||
deltas[i++] = [this.sDiskPath, this.dwChecksum, this.nCylinders, this.nHeads, this.nSectors, this.cbSector];
|
||||
if (!this.fRemote && !this.fWriteProtected) {
|
||||
var aDiskData = this.aDiskData;
|
||||
for (var iCylinder = 0; iCylinder < aDiskData.length; iCylinder++) {
|
||||
|
|
@ -1486,7 +1499,7 @@ Disk.prototype.save = function()
|
|||
*
|
||||
* The first array entry contains some disk information:
|
||||
*
|
||||
* [sDiskPath, dwChecksum]
|
||||
* [sDiskPath, dwChecksum, nCylinders, nHeads, nSectors, cbSector]
|
||||
*
|
||||
* Each subsequent entry in the supplied array contains the following:
|
||||
*
|
||||
|
|
@ -1514,16 +1527,34 @@ Disk.prototype.restore = function(deltas)
|
|||
* checking aDiskData is still a good idea, be aware that it won't necessarily avoid redundant error messages
|
||||
* (at least in the case of HDC).
|
||||
*/
|
||||
if (this.aDiskData.length && deltas && deltas.length > 0) {
|
||||
if (deltas && deltas.length > 0) {
|
||||
|
||||
var i = 0;
|
||||
var aDiskInfo = deltas[i++];
|
||||
|
||||
if (aDiskInfo && aDiskInfo.length >= 2) {
|
||||
/*
|
||||
* Before getting to the checksum, we have to deal with a new situation: restoring an uninitialized
|
||||
* disk image from a complete set of deltas. And that is only possible if the disk was saved with the
|
||||
* original disk geometry.
|
||||
*/
|
||||
if (!this.aDiskData.length && aDiskInfo.length >= 6) {
|
||||
this.create(DiskAPI.MODE.LOCAL, aDiskInfo[2], aDiskInfo[3], aDiskInfo[4], aDiskInfo[5]);
|
||||
/*
|
||||
* TODO: Consider setting a flag here that we can check at the end of the restore() function
|
||||
* that indicates we should recalculate dwChecksum, because we currently have an inconsistency
|
||||
* between local disks that are mounted via build() and the same disks that are "remounted"
|
||||
* later by this code; the former has the correct checksum, while the latter has a null checksum.
|
||||
*
|
||||
* As you can see below, we currently deal with this by simply ignoring null checksums....
|
||||
*/
|
||||
}
|
||||
/*
|
||||
* v1.01 failed to indicate an error if either one of these failure conditions occurred. Although maybe that's
|
||||
* just as well, since v1.01 also failed to properly deal with situations where the user mounted different diskette(s)
|
||||
* prior to exiting (hopefully fixed in v1.02).
|
||||
*/
|
||||
if (aDiskInfo[1] != this.dwChecksum) {
|
||||
else if (aDiskInfo[1] != null && aDiskInfo[1] != this.dwChecksum) {
|
||||
sReason = "original checksum (" + aDiskInfo[1] + ") differs from current checksum (" + this.dwChecksum + ")";
|
||||
nChanges = -1;
|
||||
}
|
||||
|
|
@ -1536,6 +1567,9 @@ Disk.prototype.restore = function(deltas)
|
|||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if (!this.aDiskData.length) nChanges = -1;
|
||||
|
||||
while (i < deltas.length && nChanges >= 0) {
|
||||
var m = 0;
|
||||
var mod = deltas[i++];
|
||||
|
|
@ -1580,6 +1614,7 @@ Disk.prototype.restore = function(deltas)
|
|||
nChanges++;
|
||||
}
|
||||
}
|
||||
|
||||
if (nChanges < 0) {
|
||||
this.controller.notice("unable to restore disk '" + this.sDiskName + ": " + sReason);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -143,19 +143,19 @@ function FDC(parmsFDC) {
|
|||
this['dmaWrite'] = this.dmaWrite;
|
||||
this['dmaFormat'] = this.dmaFormat;
|
||||
|
||||
this.configAutoMount = null;
|
||||
this.configMount = null;
|
||||
if (parmsFDC['autoMount']) {
|
||||
this.configAutoMount = parmsFDC['autoMount'];
|
||||
if (typeof this.configAutoMount == "string") {
|
||||
this.configMount = parmsFDC['autoMount'];
|
||||
if (typeof this.configMount == "string") {
|
||||
try {
|
||||
/*
|
||||
* The most likely source of any exception will be right here, where we're parsing
|
||||
* the JSON-encoded diskette data.
|
||||
*/
|
||||
this.configAutoMount = eval("(" + parmsFDC['autoMount'] + ")");
|
||||
this.configMount = eval("(" + parmsFDC['autoMount'] + ")");
|
||||
} catch (e) {
|
||||
Component.error("FDC auto-mount error: " + e.message + " (" + parmsFDC['autoMount'] + ")");
|
||||
this.configAutoMount = null;
|
||||
this.configMount = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -419,15 +419,8 @@ FDC.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, control)
|
|||
case "listDisks":
|
||||
this.bindings[sBinding] = control;
|
||||
|
||||
var addControlOption = function(sValue, sDisplay) {
|
||||
var controlOption;
|
||||
controlOption = window.document.createElement("option");
|
||||
controlOption['value'] = sValue;
|
||||
controlOption.innerHTML = sDisplay;
|
||||
control.appendChild(controlOption);
|
||||
};
|
||||
|
||||
addControlOption("?", "Remote Disk");
|
||||
this.addDiskette("Local Disk", "?");
|
||||
this.addDiskette("Remote Disk", "??");
|
||||
|
||||
control.onchange = function onChangeListDisks(event) {
|
||||
var controlDesc = fdc.bindings["descDisk"];
|
||||
|
|
@ -477,15 +470,15 @@ FDC.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, control)
|
|||
};
|
||||
return true;
|
||||
|
||||
case "loadLocal":
|
||||
case "mountDrive":
|
||||
/*
|
||||
* Check for availability of FileReader
|
||||
*/
|
||||
if (window.FileReader && window.File && window.FileList && window.Blob ) {
|
||||
if (window.FileReader && window.File && window.FileList) {
|
||||
this.bindings[sBinding] = control;
|
||||
|
||||
/*
|
||||
* Enable "Load Local File" button only if a file is actually selected
|
||||
* Enable "Mount" button only if a file is actually selected
|
||||
*/
|
||||
control.addEventListener('change', function() {
|
||||
var fieldset = control.children[0];
|
||||
|
|
@ -508,7 +501,7 @@ FDC.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, control)
|
|||
};
|
||||
}
|
||||
else {
|
||||
this.println("FileReader support not available, disabling local file load");
|
||||
if (DEBUG) this.log("FileReader support not available, Mount disabled");
|
||||
control.parentNode.removeChild(control);
|
||||
}
|
||||
return true;
|
||||
|
|
@ -824,6 +817,7 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
|
|||
var fSuccess = true;
|
||||
|
||||
drive.iDrive = iDrive;
|
||||
drive.fBusy = drive.fLocal = false;
|
||||
|
||||
if (data === undefined) {
|
||||
/*
|
||||
|
|
@ -937,15 +931,26 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
|
|||
}
|
||||
|
||||
var deltas = data[i++];
|
||||
if (deltas === Computer.VERSION_102) {
|
||||
if (typeof deltas == "boolean") {
|
||||
var fLocal = deltas;
|
||||
var sDisketteName = data[i++];
|
||||
var sDiskettePath = data[i];
|
||||
/*
|
||||
* If loadDiskette() must actually mount a *different* disk image at this late stage (ie, if it returns false),
|
||||
* then we must mark ourselves as "not ready" again, and add another "wait for ready" test in Computer before
|
||||
* finally powering the CPU. Otherwise, go ahead and restore any deltas to the current image.
|
||||
* If we're restoring a local disk image, then the entire disk contents should be captured in aDiskHistory,
|
||||
* so all we have to do is mount a blank diskette and let disk.restore() do the rest; ie, there's nothing to
|
||||
* "load" (it's a purely synchronous operation).
|
||||
*
|
||||
* Otherwise, we must call loadDiskette(); in the common case, loadDiskette() will have already "auto-mounted"
|
||||
* the diskette, so it will return true, and then we restore any deltas to the current image.
|
||||
*
|
||||
* However, if loadDiskette() returns false, then it has initiated the load for a *different* disk image,
|
||||
* so we must mark ourselves as "not ready" again, and add another "wait for ready" test in Computer before
|
||||
* finally powering the CPU.
|
||||
*/
|
||||
if (this.loadDiskette(iDrive, sDisketteName, sDiskettePath, true)) {
|
||||
if (fLocal) {
|
||||
this.mountDiskette(iDrive, sDisketteName, sDiskettePath);
|
||||
}
|
||||
else if (this.loadDiskette(iDrive, sDisketteName, sDiskettePath, true)) {
|
||||
if (drive.disk) {
|
||||
this.addDiskHistory(sDisketteName, sDiskettePath, drive.disk);
|
||||
}
|
||||
|
|
@ -1021,10 +1026,10 @@ FDC.prototype.saveDrive = function(drive)
|
|||
*
|
||||
* data[i++] = drive.disk? drive.disk.save() : null;
|
||||
*
|
||||
* To indicate this deviation, we store neither a null nor a delta array, but Computer.VERSION_102;
|
||||
* if that value is not present, then the restore code will know it's dealing with a pre-v1.02 state.
|
||||
* To indicate this deviation, we store neither a null nor a delta array, but a boolean (fLocal);
|
||||
* if that boolean is not present, then the restore code will know it's dealing with a pre-v1.02 state.
|
||||
*/
|
||||
data[i++] = Computer.VERSION_102;
|
||||
data[i++] = drive.fLocal;
|
||||
data[i++] = drive.sDisketteName;
|
||||
data[i] = drive.sDiskettePath;
|
||||
return data;
|
||||
|
|
@ -1135,9 +1140,9 @@ FDC.prototype.seekDrive = function(drive, iSector, nSectors)
|
|||
FDC.prototype.autoMount = function(fRemount)
|
||||
{
|
||||
if (!fRemount) this.cAutoMount = 0;
|
||||
if (this.configAutoMount) {
|
||||
for (var sDrive in this.configAutoMount) {
|
||||
var configDrive = this.configAutoMount[sDrive];
|
||||
if (this.configMount) {
|
||||
for (var sDrive in this.configMount) {
|
||||
var configDrive = this.configMount[sDrive];
|
||||
if (configDrive['name'] && configDrive['path']) {
|
||||
/*
|
||||
* WARNING: This conversion of drive letter to drive number, starting with A:, is very simplistic
|
||||
|
|
@ -1175,7 +1180,7 @@ FDC.prototype.loadSelectedDrive = function(sDisketteName, sDiskettePath, file)
|
|||
return;
|
||||
}
|
||||
/*
|
||||
* If the special path of "?" is selected, then we want to prompt the user for a URL. Oh, and
|
||||
* If the special path of "??" is selected, then we want to prompt the user for a URL. Oh, and
|
||||
* make sure we pass an empty string as the 2nd parameter to prompt(), so that IE won't display
|
||||
* "undefined" -- because after all, undefined and "undefined" are EXACTLY the same thing, right?
|
||||
*
|
||||
|
|
@ -1183,7 +1188,7 @@ FDC.prototype.loadSelectedDrive = function(sDisketteName, sDiskettePath, file)
|
|||
* I should do, like dynamically updating "listDisks" to include new entries, and adding new entries
|
||||
* to the save/restore data.
|
||||
*/
|
||||
if (sDiskettePath == "?") {
|
||||
if (sDiskettePath == "??") {
|
||||
sDiskettePath = window.prompt("Enter the URL of a remote disk image.", "") || "";
|
||||
if (!sDiskettePath) return;
|
||||
sDisketteName = str.getBaseName(sDiskettePath);
|
||||
|
|
@ -1212,6 +1217,23 @@ FDC.prototype.loadSelectedDrive = function(sDisketteName, sDiskettePath, file)
|
|||
this.notice("Nothing to load");
|
||||
};
|
||||
|
||||
/**
|
||||
* mountDiskette(iDrive, sDisketteName, sDiskettePath)
|
||||
*
|
||||
* @this {FDC}
|
||||
* @param {number} iDrive
|
||||
* @param {string} sDisketteName
|
||||
* @param {string} sDiskettePath
|
||||
*/
|
||||
FDC.prototype.mountDiskette = function(iDrive, sDisketteName, sDiskettePath)
|
||||
{
|
||||
var drive = this.aDrives[iDrive];
|
||||
this.unloadDrive(iDrive, true, true);
|
||||
drive.fLocal = true;
|
||||
var disk = new Disk(this, drive, DiskAPI.MODE.PRELOAD);
|
||||
this.doneLoadDiskette(drive, disk, sDisketteName, sDiskettePath);
|
||||
};
|
||||
|
||||
/**
|
||||
* loadDiskette(iDrive, sDisketteName, sDiskettePath, fAutoMount, file)
|
||||
*
|
||||
|
|
@ -1240,6 +1262,7 @@ FDC.prototype.loadDiskette = function(iDrive, sDisketteName, sDiskettePath, fAut
|
|||
this.cAutoMount++;
|
||||
this.messageDebugger("loading diskette '" + sDisketteName + "'");
|
||||
}
|
||||
drive.fLocal = !!file;
|
||||
var disk = new Disk(this, drive, DiskAPI.MODE.PRELOAD);
|
||||
disk.load(sDisketteName, sDiskettePath, file, this.doneLoadDiskette);
|
||||
return false;
|
||||
|
|
@ -1282,8 +1305,28 @@ FDC.prototype.doneLoadDiskette = function onFDCLoadNotify(drive, disk, sDiskette
|
|||
drive.disk = disk;
|
||||
drive.sDisketteName = sDisketteName;
|
||||
drive.sDiskettePath = sDiskettePath;
|
||||
|
||||
/*
|
||||
* Adding local disk image names to the disk list seems like a nice idea, but it's too confusing,
|
||||
* because then it looks like the "Load" button should be able to (re)load them, and that can NEVER
|
||||
* happen, for security reasons; local disk images can ONLY be loaded via the "Mount" button after
|
||||
* the user has selected them via the "Choose File" button.
|
||||
*
|
||||
* this.addDiskette(sDisketteName, sDiskettePath);
|
||||
*
|
||||
* So we're going to take a different approach: when displayDiskette() is asked to display the name
|
||||
* of a local disk image, it will map all such disks to "Local Disk", and any attempt to "Load" such
|
||||
* a disk, will essentially result in a "Disk not found" error.
|
||||
*/
|
||||
|
||||
this.addDiskHistory(sDisketteName, sDiskettePath, disk);
|
||||
|
||||
/*
|
||||
* For a local disk (ie, one loaded via mountDiskette()), the disk.restore() performed by addDiskHistory()
|
||||
* may have altered the disk geometry, so refresh the disk info.
|
||||
*/
|
||||
aDiskInfo = disk.info();
|
||||
|
||||
/*
|
||||
* Clearly, a successful mount implies a disk change, and I suppose that, technically, an *unsuccessful*
|
||||
* mount should imply the same, but what would the real-world analog be? Inserting a piece of cardboard
|
||||
|
|
@ -1310,6 +1353,9 @@ FDC.prototype.doneLoadDiskette = function onFDCLoadNotify(drive, disk, sDiskette
|
|||
drive.nDiskHeads = aDiskInfo[1];
|
||||
drive.nDiskSectors = aDiskInfo[2];
|
||||
}
|
||||
else {
|
||||
drive.fLocal = false;
|
||||
}
|
||||
|
||||
if (drive.fAutoMount) {
|
||||
drive.fAutoMount = false;
|
||||
|
|
@ -1319,6 +1365,26 @@ FDC.prototype.doneLoadDiskette = function onFDCLoadNotify(drive, disk, sDiskette
|
|||
this.displayDiskette(drive.iDrive);
|
||||
};
|
||||
|
||||
/**
|
||||
* addDiskette(sName, sPath)
|
||||
*
|
||||
* @param {string} sName
|
||||
* @param {string} sPath
|
||||
*/
|
||||
FDC.prototype.addDiskette = function(sName, sPath)
|
||||
{
|
||||
var controlDisks = this.bindings["listDisks"];
|
||||
if (controlDisks) {
|
||||
for (var i = 0; i < controlDisks.options.length; i++) {
|
||||
if (controlDisks.options[i].value == sPath) return;
|
||||
}
|
||||
var controlOption = window.document.createElement("option");
|
||||
controlOption['value'] = sPath;
|
||||
controlOption.innerHTML = sName;
|
||||
controlDisks.appendChild(controlOption);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* displayDiskette(iDrive, fUpdateDrive)
|
||||
*
|
||||
|
|
@ -1344,9 +1410,10 @@ FDC.prototype.displayDiskette = function(iDrive, fUpdateDrive)
|
|||
*/
|
||||
var i;
|
||||
var iDriveSelected = parseInt(controlDrives.value, 10);
|
||||
var sTargetPath = (drive.fLocal? "?" : drive.sDiskettePath);
|
||||
if (!isNaN(iDriveSelected) && iDriveSelected == iDrive) {
|
||||
for (i = 0; i < controlDisks.options.length; i++) {
|
||||
if (controlDisks.options[i].value == drive.sDiskettePath) {
|
||||
if (controlDisks.options[i].value == sTargetPath) {
|
||||
if (controlDisks.selectedIndex != i) {
|
||||
controlDisks.selectedIndex = i;
|
||||
}
|
||||
|
|
@ -1388,6 +1455,7 @@ FDC.prototype.unloadDrive = function(iDrive, fAutoUnload, fQuiet)
|
|||
drive.sDisketteName = "";
|
||||
drive.sDiskettePath = "";
|
||||
drive.disk = null;
|
||||
drive.fLocal = false;
|
||||
|
||||
this.regInput |= FDC.REG_INPUT.DISK_CHANGE;
|
||||
|
||||
|
|
@ -1435,6 +1503,7 @@ FDC.prototype.unloadAllDrives = function(fDiscard)
|
|||
FDC.prototype.addDiskHistory = function(sDisketteName, sDiskettePath, disk)
|
||||
{
|
||||
var i;
|
||||
Component.assert(!!sDiskettePath);
|
||||
for (i = 0; i < this.aDiskHistory.length; i++) {
|
||||
if (this.aDiskHistory[i][1] == sDiskettePath) {
|
||||
var nChanges = disk.restore(this.aDiskHistory[i][2]);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,11 @@
|
|||
font-family: Monaco, monospace;
|
||||
font-size: x-small;
|
||||
}
|
||||
.pcjs-fieldset {
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.pcjs-flag {
|
||||
font-family: "Lucida Console", monospace;
|
||||
font-size: small;
|
||||
|
|
|
|||
|
|
@ -358,7 +358,7 @@
|
|||
</xsl:when>
|
||||
<xsl:when test="@type = 'file'">
|
||||
<form class="{$APPCLASS}-{@class}" data-value="{$type},{$binding}">
|
||||
<fieldset>
|
||||
<fieldset class="{$APPCLASS}-fieldset">
|
||||
<input type="file"/>
|
||||
<input type="submit" value="Mount" disabled="true"/>
|
||||
</fieldset>
|
||||
|
|
|
|||
Loading…
Reference in a new issue