From 5441c6346bb58b2ebd11a45d7d036be87bc57334 Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Thu, 1 Jun 2017 20:59:41 -0700 Subject: [PATCH] Improved geometry calculation for all disk images, not just those listed in DiskAPI.js --- devices/pcx86/machine/5160/cga/README.md | 2 +- modules/diskdump/lib/diskdump.js | 58 ++++++++++++------- modules/pcx86/lib/disk.js | 2 +- modules/pdp11/lib/disk.js | 2 +- modules/shared/lib/diskapi.js | 73 ++++++++++++------------ 5 files changed, 76 insertions(+), 61 deletions(-) diff --git a/devices/pcx86/machine/5160/cga/README.md b/devices/pcx86/machine/5160/cga/README.md index 5db942fa9..0c5077606 100644 --- a/devices/pcx86/machine/5160/cga/README.md +++ b/devices/pcx86/machine/5160/cga/README.md @@ -12,10 +12,10 @@ IBM PC XT with Color Graphics (CGA) Display All our Color Graphics (CGA) configurations of the IBM PC XT (Model 5160) are located here, including: * [IBM PC XT (256Kb, 10Mb Drive) with Color Display](/devices/pcx86/machine/5160/cga/256kb/demo/) ([Debugger](/devices/pcx86/machine/5160/cga/256kb/demo/debugger/)) +* [IBM PC XT (256Kb, 10Mb Drive) Machine Array with CGA Displays](/devices/pcx86/machine/5160/cga/256kb/array/) * [IBM PC XT (256Kb, 10Mb Drive) with Color Display and Soft Keyboard (Debugger)](/devices/pcx86/machine/5160/cga/256kb/softkbd/) * [IBM PC XT (256Kb, 10Mb Drive) with Color Display running Windows 1.01](/devices/pcx86/machine/5160/cga/256kb/win101/) ([Debugger](/devices/pcx86/machine/5160/cga/256kb/win101/debugger/)) * [IBM PC XT (256Kb, 10Mb Drive) with Color Display and Soft Keyboard (Debugger)](/devices/pcx86/machine/5160/cga/256kb/win101/softkbd/) * [IBM PC XT (512Kb, 10Mb Drive) with Color Display and Soft Keyboard (Debugger)](/devices/pcx86/machine/5160/cga/512kb/win101/softkbd/) * [IBM PC XT (640Kb, 10Mb Drive) with Color Display](/devices/pcx86/machine/5160/cga/640kb/) ([Debugger](/devices/pcx86/machine/5160/cga/640kb/debugger/)) * [IBM PC XT (640Kb, 10Mb Drive) with Color Display and Soft Keyboard (Debugger)](/devices/pcx86/machine/5160/cga/640kb/softkbd/) -* [IBM PC XT (256Kb, 10Mb Drive) Machine Array with CGA Displays](/devices/pcx86/machine/5160/cga/256kb/array/) diff --git a/modules/diskdump/lib/diskdump.js b/modules/diskdump/lib/diskdump.js index 714c5d7ed..7444168dd 100644 --- a/modules/diskdump/lib/diskdump.js +++ b/modules/diskdump/lib/diskdump.js @@ -527,6 +527,22 @@ DiskDump.aDefaultBPBs = [ 0x02, 0x00, // 0x1A: number of heads (2) 0x00, 0x00, 0x00, 0x00 // 0x1C: number of hidden sectors (always 0 for non-partitioned media) ], + /* + * Here's some useful background information on a 10Mb PC XT fixed disk, partitioned with a single DOS partition. + * + * The BPB for a 10Mb "type 3" PC XT drive specifies 0x5103 or 20739 for TOTAL_SECS, which is the partition + * size in sectors (10,618,368 bytes), whereas the total disk size is 20808 sectors (10,653,696 bytes). The partition + * is 69 sectors smaller than the disk because the first sector is reserved for the MBR and 68 sectors (the entire last + * cylinder) are reserved for diagnostics, head parking, etc. This cylinder usage is confirmed by FDISK, which reports + * that 305 cylinders (not 306) are assigned to the DOS partition. + * + * That 69-sector overhead is NOT the overhead incurred by the FAT file system, which is the boot sector (1), FAT + * sectors (16), and root directory sectors (32), for a total of 49 sectors, leaving 20739 - 49 or 20690 sectors. + * However, free space is measured in clusters, not sectors, and the partition uses 8 sectors/cluster, leaving room + * for 2586.25 clusters. Since a fractional cluster is not allowed, another 2 sectors are lost to FAT overhead, + * for a total of 51 sectors. So actual free space is (20739 - 51) * 512, or 10,592,256 bytes -- which is exactly + * what is reported as the available space on a freshly formatted PC XT 10Mb fixed disk. + */ [ // define BPB for 10Mb hard drive 0xEB, 0xFE, 0x90, // 0x00: JMP instruction, following by 8-byte OEM signature 0x50, 0x43, 0x4A, 0x53, 0x2E, 0x4F, 0x52, 0x47, // PCJS_OEM @@ -2496,7 +2512,6 @@ 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; @@ -2505,7 +2520,7 @@ DiskDump.prototype.convertToJSON = function() var cbSector = 512; // default sector size var bMediaType = 0; var offBootSector = 0; - var cbDiskData = this.bufDisk.length; + var cbDiskData = this.bufDisk.length, cbPartition = cbDiskData; if (cbDiskData >= 3000000) { // arbitrary threshold between diskette image sizes and hard drive image sizes var wSig = this.bufDisk.readUInt16LE(DiskAPI.BOOT.SIG_OFFSET); @@ -2517,8 +2532,7 @@ DiskDump.prototype.convertToJSON = function() for (var offEntry = 0x1BE; offEntry <= 0x1EE; offEntry += 0x10) { if (this.bufDisk.readUInt8(offEntry) >= 0x80) { offBootSector = this.bufDisk.readUInt32LE(offEntry + 0x08) * cbSector; - cbDiskData = this.bufDisk.readUInt32LE(offEntry + 0x0C) * cbSector; - fMBR = true; + cbPartition = this.bufDisk.readUInt32LE(offEntry + 0x0C) * cbSector; break; } } @@ -2548,7 +2562,7 @@ DiskDump.prototype.convertToJSON = function() * image whose logical format doesn't agree with its physical structure. */ var fXDFOutput = false; - var diskFormat = DiskAPI.DISK_FORMATS[cbDiskData]; + var diskFormat = DiskAPI.GEOMETRIES[cbDiskData]; if (diskFormat) { nCylinders = diskFormat[0]; nHeads = diskFormat[1]; @@ -2579,11 +2593,7 @@ DiskDump.prototype.convertToJSON = function() 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 + ")"); @@ -2599,9 +2609,13 @@ DiskDump.prototype.convertToJSON = function() } } else { - nCylinders = nCylindersBPB; nHeads = nHeadsBPB; nSectorsPerTrack = nSectorsPerTrackBPB; + nCylinders = cbDiskData / (nHeads * nSectorsPerTrack * cbSector); + if (nCylinders != (nCylinders|0)) { + DiskDump.logWarning("total cylinders (" + nCylinders + ") not a multiple of heads (" + nHeads + ") and sectors/track (" + nSectorsPerTrack + ")"); + nCylinders |= 0; + } bMediaType = bMediaTypeBPB; } @@ -2645,8 +2659,11 @@ DiskDump.prototype.convertToJSON = function() if (bMediaType) { for (i = 0; i < DiskDump.aDefaultBPBs.length; i++) { if (DiskDump.aDefaultBPBs[i][DiskAPI.BPB.MEDIA_TYPE] == bMediaType) { - iBPB = i; - break; + var cbDiskBPB = (DiskDump.aDefaultBPBs[i][DiskAPI.BPB.TOTAL_SECS] + (DiskDump.aDefaultBPBs[i][DiskAPI.BPB.TOTAL_SECS + 1] * 0x100)) * cbSector; + if (cbDiskBPB == cbDiskData) { + iBPB = i; + break; + } } } } @@ -2696,15 +2713,14 @@ DiskDump.prototype.convertToJSON = function() DiskDump.logWarning("unrecognized boot sector: " + str.toHexByte(bByte0) + "," + str.toHexByte(bByte1)); } - if (fBPBExists) { - /* - * Overwrite the OEM string with our own, so that people know how the image originated. We do this - * only for disks with pre-existing BPBs; it's not safe for older disks (and non-DOS disks, obviously). - */ - this.bufDisk.write(DiskDump.PCJS_OEM, DiskAPI.BOOT.OEM_STRING, DiskDump.PCJS_OEM.length); - } } - + if (fBPBExists) { + /* + * Overwrite the OEM string with our own, so that people know how the image originated. We do this + * only for disks with pre-existing BPBs; it's not safe for pre-2.0 disks (and non-DOS disks, obviously). + */ + this.bufDisk.write(DiskDump.PCJS_OEM, DiskAPI.BOOT.OEM_STRING + offBootSector, DiskDump.PCJS_OEM.length); + } if (!nHeads) { /* * Next, check for a DSK header (an old private format I used to use, which begins with either diff --git a/modules/pcx86/lib/disk.js b/modules/pcx86/lib/disk.js index c0958c3e4..a70c99beb 100644 --- a/modules/pcx86/lib/disk.js +++ b/modules/pcx86/lib/disk.js @@ -545,7 +545,7 @@ class Disk extends Component { { var disk; var cbDiskData = buffer? buffer.byteLength : 0; - var diskFormat = DiskAPI.DISK_FORMATS[cbDiskData]; + var diskFormat = DiskAPI.GEOMETRIES[cbDiskData]; if (diskFormat) { this.nCylinders = diskFormat[0]; diff --git a/modules/pdp11/lib/disk.js b/modules/pdp11/lib/disk.js index 0f80fa19c..cbccd5326 100644 --- a/modules/pdp11/lib/disk.js +++ b/modules/pdp11/lib/disk.js @@ -335,7 +335,7 @@ class DiskPDP11 extends Component { { var disk; var cbDiskData = buffer? buffer.byteLength : 0; - var diskFormat = DiskAPI.DISK_FORMATS[cbDiskData]; + var diskFormat = DiskAPI.GEOMETRIES[cbDiskData]; if (diskFormat) { this.nCylinders = diskFormat[0]; diff --git a/modules/shared/lib/diskapi.js b/modules/shared/lib/diskapi.js index 093d81652..19f3981d4 100644 --- a/modules/shared/lib/diskapi.js +++ b/modules/shared/lib/diskapi.js @@ -68,48 +68,12 @@ var DiskAPI = { } }; -/* - * Common (supported) diskette formats - * - * For no particular reason that I can recall, each entry in DISK_FORMATS is an array of values in "CHS" order: - * - * [# cylinders, # heads, # sectors/track, # bytes/sector, media type] - * - * If the 4th value is omitted, the sector size is assumed to be 512. The order of these "geometric" values mirrors - * the structure of our JSON-encoded disk images, which consist of an array of cylinders, each of which is an array of - * heads, each of which is an array of sector objects. - */ -DiskAPI.DISK_FORMATS = { - 163840: [40,1,8,,0xFE], // media type 0xFE: 40 cylinders, 1 head (single-sided), 8 sectors/track, ( 320 total sectors x 512 bytes/sector == 163840) - 184320: [40,1,9,,0xFC], // media type 0xFC: 40 cylinders, 1 head (single-sided), 9 sectors/track, ( 360 total sectors x 512 bytes/sector == 184320) - 327680: [40,2,8,,0xFF], // media type 0xFF: 40 cylinders, 2 heads (double-sided), 8 sectors/track, ( 640 total sectors x 512 bytes/sector == 327680) - 368640: [40,2,9,,0xFD], // media type 0xFD: 40 cylinders, 2 heads (double-sided), 9 sectors/track, ( 720 total sectors x 512 bytes/sector == 368640) - 737280: [80,2,9,,0xF9], // media type 0xF9: 80 cylinders, 2 heads (double-sided), 9 sectors/track, (1440 total sectors x 512 bytes/sector == 737280) - 1228800: [80,2,15,,0xF9], // media type 0xF9: 80 cylinders, 2 heads (double-sided), 15 sectors/track, (2400 total sectors x 512 bytes/sector == 1228800) - 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 some common partition sizes, which we explicitly map to CHS values, since MBR/BPB values - * can mislead us when attempting to determine the exact disk geometry. TODO: Investigate. Is it possible that - * usable space on early hard disks was reduced to reserve the last cylinder for diagnostics, head parking, etc? - */ - 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. - */ - 256256: [77, 1,26,128], // RX01 single-platter diskette: 77 tracks, 1 head, 26 sectors/track, 128 bytes/sector, for a total of 256256 bytes - 2494464: [203,2,12,512], // RK03 single-platter disk cartridge: 203 tracks, 2 heads, 12 sectors/track, 512 bytes/sector, for a total of 2494464 bytes - 5242880: [256,2,40,256], // RL01K single-platter disk cartridge: 256 tracks, 2 heads, 40 sectors/track, 256 bytes/sector, for a total of 5242880 bytes - 10485760:[512,2,40,256] // RL02K single-platter disk cartridge: 512 tracks, 2 heads, 40 sectors/track, 256 bytes/sector, for a total of 10485760 bytes -}; - /* * TODO: Eventually, our tools will need to support looking up disk formats by "model" rather than by raw disk size, * because obviously multiple disk geometries can yield the same raw disk size. For each conflict that arises, I'll * probably create a fake (approximate) disk size entry above, and then create a mapping to that approximate size below. */ -DiskAPI.DISK_MODELS = { +DiskAPI.MODELS = { "RL01": 5242880, "RL02": 10485760 }; @@ -177,6 +141,41 @@ DiskAPI.BPB = { LARGE_SECS: 0x020 // 4 bytes (DOS 3.31 and up): number of sectors if TOTAL_SECS is zero }; +/* + * Common (supported) diskette geometries. + * + * Each entry in GEOMETRIES is an array of values in "CHS" order: + * + * [# cylinders, # heads, # sectors/track, # bytes/sector, media type] + * + * If the 4th value is omitted, the sector size is assumed to be 512. The order of these "geometric" values mirrors + * the structure of our JSON-encoded disk images, which consist of an array of cylinders, each of which is an array of + * heads, each of which is an array of sector objects. + */ +DiskAPI.GEOMETRIES = { + 163840: [40,1,8,,0xFE], // media type 0xFE: 40 cylinders, 1 head (single-sided), 8 sectors/track, ( 320 total sectors x 512 bytes/sector == 163840) + 184320: [40,1,9,,0xFC], // media type 0xFC: 40 cylinders, 1 head (single-sided), 9 sectors/track, ( 360 total sectors x 512 bytes/sector == 184320) + 327680: [40,2,8,,0xFF], // media type 0xFF: 40 cylinders, 2 heads (double-sided), 8 sectors/track, ( 640 total sectors x 512 bytes/sector == 327680) + 368640: [40,2,9,,0xFD], // media type 0xFD: 40 cylinders, 2 heads (double-sided), 9 sectors/track, ( 720 total sectors x 512 bytes/sector == 368640) + 737280: [80,2,9,,0xF9], // media type 0xF9: 80 cylinders, 2 heads (double-sided), 9 sectors/track, (1440 total sectors x 512 bytes/sector == 737280) + 1228800: [80,2,15,,0xF9], // media type 0xF9: 80 cylinders, 2 heads (double-sided), 15 sectors/track, (2400 total sectors x 512 bytes/sector == 1228800) + 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 some common disk sizes and their CHS values, since missing or bogus MBR and/or BPB values + * might mislead us when attempting to determine the exact disk geometry. + */ + 10653696:[306,4,17], // PC XT 10Mb hard drive (type 3) + 21411840:[615,4,17], // PC AT 20Mb hard drive (type 2) + /* + * Assorted DEC disk formats. + */ + 256256: [77, 1,26,128], // RX01 single-platter diskette: 77 tracks, 1 head, 26 sectors/track, 128 bytes/sector, for a total of 256256 bytes + 2494464: [203,2,12,512], // RK03 single-platter disk cartridge: 203 tracks, 2 heads, 12 sectors/track, 512 bytes/sector, for a total of 2494464 bytes + 5242880: [256,2,40,256], // RL01K single-platter disk cartridge: 256 tracks, 2 heads, 40 sectors/track, 256 bytes/sector, for a total of 5242880 bytes + 10485760:[512,2,40,256] // RL02K single-platter disk cartridge: 512 tracks, 2 heads, 40 sectors/track, 256 bytes/sector, for a total of 10485760 bytes +}; + /* * Media descriptor bytes for DOS-compatible FAT-formatted disks (stored in the first byte of the FAT) */