Added an empty 10Mb disk image formatted by PC-DOS 2.0 (currently used by one machine: 5160/cga/640kb); also fixed some issues uncovered in the process of creating the disk image (eg, a bug in the HDC's save logic when no image has been loaded), and made some fixes and optimizations in DiskDump
This commit is contained in:
parent
8ef74395d4
commit
3e1aa196bd
13 changed files with 1871 additions and 1832 deletions
|
|
@ -539,12 +539,17 @@ DiskDump.aDefaultBPBs = [
|
|||
0x03, 0x51, // 0x13: number of sectors (0x5103 or 20739; * 512 bytes/sector = 10,618,368 bytes = 10,369Kb = 10Mb)
|
||||
0xF8, // 0x15: media type (eg, 0xF8: hard drive w/FAT12)
|
||||
0x08, 0x00, // 0x16: sectors per FAT (8)
|
||||
// Wikipedia (http://en.wikipedia.org/wiki/File_Allocation_Table#BIOS_Parameter_Block) implies everything past this point was introduced
|
||||
// post-DOS 2.0. I think that's wrong, because I just formatted a diskette with PC-DOS 2.0 and it properly initialized the next 3 fields as well.
|
||||
//
|
||||
// Wikipedia (http://en.wikipedia.org/wiki/File_Allocation_Table#BIOS_Parameter_Block) implies everything past
|
||||
// this point was introduced post-DOS 2.0. I think that's wrong, because I just formatted a diskette with PC-DOS 2.0
|
||||
// and it properly initialized the next 3 fields as well. TODO: Investigate PC-DOS 2.0 BPB behavior.
|
||||
//
|
||||
0x11, 0x00, // 0x18: sectors per track (17)
|
||||
0x04, 0x00, // 0x1A: number of heads (4)
|
||||
// PC-DOS 2.0 actually stored 0x01, 0x00, 0x80, 0x00 here, so you can't always assume that anything past offset 0x1E is part of the BPB;
|
||||
// requires further investigation.
|
||||
//
|
||||
// PC-DOS 2.0 actually stored 0x01, 0x00, 0x80, 0x00 here, so you can't always assume that anything past offset
|
||||
// 0x1E is part of the BPB. TODO: Investigate PC-DOS 2.0 BPB behavior.
|
||||
//
|
||||
0x01, 0x00, 0x00, 0x00 // 0x1C: number of hidden sectors (always 0 for non-partitioned media)
|
||||
]
|
||||
];
|
||||
|
|
@ -2488,7 +2493,9 @@ DiskDump.prototype.convertToJSON = function()
|
|||
}
|
||||
|
||||
var json = null;
|
||||
var fOptimize = true; // if true, leave out any properties that are defaults
|
||||
try {
|
||||
var fMBR = false;
|
||||
var nHeads = 0;
|
||||
var nCylinders = 0;
|
||||
var nSectorsPerTrack = 0;
|
||||
|
|
@ -2510,6 +2517,7 @@ DiskDump.prototype.convertToJSON = function()
|
|||
if (this.bufDisk.readUInt8(offEntry) >= 0x80) {
|
||||
offBootSector = this.bufDisk.readUInt32LE(offEntry + 0x08) * cbSector;
|
||||
cbDiskData = this.bufDisk.readUInt32LE(offEntry + 0x0C) * cbSector;
|
||||
fMBR = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -2568,22 +2576,25 @@ DiskDump.prototype.convertToJSON = function()
|
|||
fBPBExists = true;
|
||||
var bMediaTypeBPB = this.bufDisk.readUInt8(offBootSector + DiskAPI.BPB.MEDIA_TYPE);
|
||||
var nSectorsTotalBPB = this.bufDisk.readUInt16LE(offBootSector + DiskAPI.BPB.TOTAL_SECS);
|
||||
|
||||
var nSectorsPerCylinderBPB = nSectorsPerTrackBPB * nHeadsBPB;
|
||||
var nCylindersBPB = Math.floor(nSectorsTotalBPB / nSectorsPerCylinderBPB);
|
||||
|
||||
/*
|
||||
* TODO: Detect newer BPBs (ie, where the HIDDEN_SECS and LARGE_SECS fields are valid),
|
||||
* so that we can determine the true geometry of the disk, instead of having to rely on our
|
||||
* DISK_FORMATS table.
|
||||
*/
|
||||
if (diskFormat) {
|
||||
if (nCylinders != nCylindersBPB) {
|
||||
DiskDump.logWarning("BPB cylinders (" + nCylindersBPB + ") do not match actual cylinders: " + nCylinders);
|
||||
DiskDump.logWarning("BPB cylinders (" + nCylindersBPB + ") do not match actual cylinders (" + nCylinders + ")");
|
||||
}
|
||||
if (nHeads != nHeadsBPB) {
|
||||
DiskDump.logWarning("BPB heads (" + nHeadsBPB + ") do not match actual heads: " + nHeads);
|
||||
DiskDump.logWarning("BPB heads (" + nHeadsBPB + ") do not match actual heads (" + nHeads + ")");
|
||||
}
|
||||
if (nSectorsPerTrack != nSectorsPerTrackBPB) {
|
||||
DiskDump.logWarning("BPB sectors/track (" + nSectorsPerTrackBPB + ") do not match actual sectors/track: " + nSectorsPerTrack);
|
||||
DiskDump.logWarning("BPB sectors/track (" + nSectorsPerTrackBPB + ") do not match actual sectors/track (" + nSectorsPerTrack + ")");
|
||||
}
|
||||
if (bMediaType && bMediaType != bMediaTypeBPB) {
|
||||
DiskDump.logWarning("BPB media type (" + bMediaTypeBPB + ") do not match actual media type: " + bMediaType);
|
||||
DiskDump.logWarning("BPB media type (" + bMediaTypeBPB + ") do not match actual media type (" + bMediaType + ")");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -2640,7 +2651,11 @@ DiskDump.prototype.convertToJSON = function()
|
|||
}
|
||||
if (iBPB >= 0) {
|
||||
if (fBPBExists) {
|
||||
for (i = DiskAPI.BPB.SECTOR_BYTES; i < DiskAPI.BPB.LARGE_SECS; i++) {
|
||||
/*
|
||||
* In deference to the PC-DOS 2.0 anomaly noted above (see "Investigate PC-DOS 2.0 BPB behavior" above),
|
||||
* we limit the following byte verification to the offset of LARGE_SECS, less two bytes.
|
||||
*/
|
||||
for (i = DiskAPI.BPB.SECTOR_BYTES; i < DiskAPI.BPB.LARGE_SECS - 2; i++) {
|
||||
var bDefault = DiskDump.aDefaultBPBs[iBPB][i];
|
||||
var bActual = this.bufDisk.readUInt8(offBootSector + i);
|
||||
if (bDefault != bActual) {
|
||||
|
|
@ -2841,21 +2856,25 @@ DiskDump.prototype.convertToJSON = function()
|
|||
|
||||
bufSector = bufTrack.slice(offSector, offSector + cbSectorThisTrack);
|
||||
|
||||
if (bMediaType && !iCylinder && !iHead && iSector == 2) {
|
||||
if (bMediaType && !iCylinder && !iHead && iSector == ((offBootSector/cbSector)|0) + 2) {
|
||||
var bFATType = bufSector.readUInt8(0);
|
||||
if (bMediaType != bFATType) {
|
||||
DiskDump.logWarning("wrong media type (" + str.toHexByte(bFATType) + ") in FAT, expected " + str.toHexByte(bMediaType));
|
||||
DiskDump.logWarning("media byte mismatch (" + str.toHexByte(bFATType) + ") in FAT, expected " + str.toHexByte(bMediaType));
|
||||
}
|
||||
bMediaType = 0;
|
||||
}
|
||||
|
||||
if (this.fJSONNative) {
|
||||
sector['sector'] = nSector;
|
||||
sector['length'] = cbSectorThisTrack;
|
||||
if (!fOptimize || cbSectorThisTrack != 512) {
|
||||
sector['length'] = cbSectorThisTrack;
|
||||
}
|
||||
} else {
|
||||
json += (iSector == 1? this.dumpLine(2, "{") : "");
|
||||
json += this.dumpLine(0, '"sector":' + this.sJSONWhitespace + nSector + ",");
|
||||
json += this.dumpLine(0, '"length":' + this.sJSONWhitespace + cbSectorThisTrack + ",");
|
||||
if (!fOptimize || cbSectorThisTrack != 512) {
|
||||
json += this.dumpLine(0, '"length":' + this.sJSONWhitespace + cbSectorThisTrack + ",");
|
||||
}
|
||||
}
|
||||
|
||||
var aTrim = this.trimSector(bufSector, cbSectorThisTrack);
|
||||
|
|
@ -2863,28 +2882,33 @@ DiskDump.prototype.convertToJSON = function()
|
|||
var cbBuffer = cbSectorThisTrack;
|
||||
if (dwPattern !== null) {
|
||||
cbBuffer = aTrim[1];
|
||||
if (this.fJSONNative) {
|
||||
sector['pattern'] = dwPattern;
|
||||
} else {
|
||||
json += this.dumpLine(0, '"pattern":' + this.sJSONWhitespace + dwPattern + ",");
|
||||
if (!fOptimize || dwPattern) {
|
||||
if (this.fJSONNative) {
|
||||
sector['pattern'] = dwPattern;
|
||||
} else {
|
||||
json += this.dumpLine(0, '"pattern":' + this.sJSONWhitespace + dwPattern + ",");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.fJSONNative) {
|
||||
var dataSector = [];
|
||||
sector['data'] = dataSector;
|
||||
for (var off = 0; off < cbBuffer; off += 4) {
|
||||
dataSector.push(bufSector.readInt32LE(off));
|
||||
if (!fOptimize || cbBuffer) {
|
||||
var dataSector = [];
|
||||
sector['data'] = dataSector;
|
||||
for (var off = 0; off < cbBuffer; off += 4) {
|
||||
dataSector.push(bufSector.readInt32LE(off));
|
||||
}
|
||||
}
|
||||
aSectors[iSector-1] = sector;
|
||||
aSectors[iSector - 1] = sector;
|
||||
} else {
|
||||
if (this.sFormat == DumpAPI.FORMAT.BYTES) {
|
||||
json += this.dumpBuffer("bytes", bufSector, cbBuffer, 1, offSector);
|
||||
} else {
|
||||
/*
|
||||
* TODO: Assert that sFormat is FORMAT_JSON or FORMAT_DATA (both use the same dword format)
|
||||
*/
|
||||
json += this.dumpBuffer("data", bufSector, cbBuffer, 4, offSector);
|
||||
if (!fOptimize || cbBuffer) {
|
||||
if (this.sFormat == DumpAPI.FORMAT.BYTES) {
|
||||
json += this.dumpBuffer("bytes", bufSector, cbBuffer, 1, offSector);
|
||||
} else {
|
||||
/*
|
||||
* TODO: Assert that sFormat is FORMAT_JSON or FORMAT_DATA (both use the same dword format)
|
||||
*/
|
||||
json += this.dumpBuffer("data", bufSector, cbBuffer, 4, offSector);
|
||||
}
|
||||
}
|
||||
json += (iSector < nSectorsThisTrack? this.dumpLine(0, "},{") : this.dumpLine(-2, "}"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -340,7 +340,8 @@ class FDC extends Component {
|
|||
var drive = fdc.aDrives[iDriveSelected];
|
||||
if (drive) {
|
||||
/*
|
||||
* Note the similarity (and hence factoring opportunity) between this code and the HDC's "saveHD*" binding.
|
||||
* Note the similarity (and hence factoring opportunity) between this code and the HDC's
|
||||
* "saveHD*" binding.
|
||||
*/
|
||||
var disk = drive.disk;
|
||||
if (disk) {
|
||||
|
|
|
|||
|
|
@ -134,11 +134,22 @@ class HDC extends Component {
|
|||
var drive = hdc.aDrives && hdc.aDrives[iDrive];
|
||||
if (drive && drive.disk) {
|
||||
/*
|
||||
* Note the similarity (and hence factoring opportunity) between this code and the FDC's "saveDisk" binding.
|
||||
* Note the similarity (and hence factoring opportunity) between this code and the FDC's
|
||||
* "saveDisk" binding.
|
||||
*
|
||||
* One important difference between the FDC and the HDC is that an FDC may or may not contain
|
||||
* a disk, whereas an HDC always contains a disk. However, the contents of an HDC's disk may
|
||||
* never have been initialized with the contents of an external disk image, and therefore the
|
||||
* disk's sDiskFile/sDiskPath properties may be undefined. sDiskName should always be defined
|
||||
* though, defaulting to the name of the drive (eg, "10Mb Hard Disk").
|
||||
*/
|
||||
var disk = drive.disk;
|
||||
if (DEBUG) hdc.println("saving disk " + disk.sDiskPath + "...");
|
||||
var sAlert = Web.downloadFile(disk.encodeAsBase64(), "octet-stream", true, disk.sDiskFile.replace(".json", ".img"));
|
||||
var sDiskName = disk.sDiskFile || disk.sDiskName;
|
||||
var i = sDiskName.lastIndexOf('.');
|
||||
if (i >= 0) sDiskName = sDiskName.substr(0, i);
|
||||
sDiskName += ".img";
|
||||
if (DEBUG) hdc.println("saving disk " + sDiskName + "...");
|
||||
var sAlert = Web.downloadFile(disk.encodeAsBase64(), "octet-stream", true, sDiskName);
|
||||
Component.alertUser(sAlert);
|
||||
} else {
|
||||
hdc.notice("Hard drive " + iDrive + " is not available.");
|
||||
|
|
|
|||
|
|
@ -89,8 +89,10 @@ DiskAPI.DISK_FORMATS = {
|
|||
1474560: [80,2,18,,0xF0], // media type 0xF0: 80 cylinders, 2 heads (double-sided), 18 sectors/track, (2880 total sectors x 512 bytes/sector == 1474560)
|
||||
2949120: [80,2,36,,0xF0], // media type 0xF0: 80 cylinders, 2 heads (double-sided), 36 sectors/track, (5760 total sectors x 512 bytes/sector == 2949120)
|
||||
/*
|
||||
* The following are common early hard drive sizes, which we explicitly map to CHS values, since the BPB can mislead us when attempting to calculate total cylinders
|
||||
* The following are some common partition sizes, which we explicitly map to CHS values, since the MBR and/or BPB
|
||||
* can mislead us when attempting to calculate total cylinders.
|
||||
*/
|
||||
10618368:[306,4,17], // PC XT 10Mb hard drive (type 3)
|
||||
21368320:[615,4,17], // PC AT 20Mb hard drive (type 2)
|
||||
/*
|
||||
* Assorted DEC disk formats.
|
||||
|
|
@ -164,7 +166,7 @@ DiskAPI.BPB = {
|
|||
TRACK_SECS: 0x018, // 2 bytes: sectors per track (eg, 8)
|
||||
TOTAL_HEADS: 0x01A, // 2 bytes: number of heads (eg, 1)
|
||||
HIDDEN_SECS: 0x01C, // 4 bytes: number of hidden sectors (always 0 for non-partitioned media)
|
||||
LARGE_SECS: 0x020 // 4 bytes: number of sectors if TOTAL_SECS is zero
|
||||
LARGE_SECS: 0x020 // 4 bytes: number of sectors if TOTAL_SECS is zero (TODO: Verify the true extent of DOS 2.0 BPBs; they may have stopped with HIDDEN_SECS, which is what DiskDump assumes)
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue