PC-DOS 7.00 SETUP is making progress, but XDF support is still lacking

This commit is contained in:
Jeff Parsons 2014-10-22 00:37:31 -05:00 committed by jeffpar
commit 3bbbd4e9b1
14 changed files with 648 additions and 570 deletions

View file

@ -2062,18 +2062,14 @@ DiskDump.prototype.convertToJSON = function()
var json = null;
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;
var cbDiskData = this.bufDisk.length;
if (cbDiskData >= 3000000) { // arbitrary threshold between diskette image sizes and hard disk image sizes
/*
@ -2109,6 +2105,7 @@ DiskDump.prototype.convertToJSON = function()
* but at some point, we'll need to perform more general calculations to properly deal with ANY disk
* image whose logical format doesn't agree with its physical structure.
*/
var fXDF = false;
var oDisketteFormat = DiskDump.DisketteFormats[cbDiskData];
if (oDisketteFormat) {
nHeads = oDisketteFormat[0];
@ -2131,30 +2128,58 @@ DiskDump.prototype.convertToJSON = function()
nHeads = nHeadsBPB;
nCylinders = Math.floor(nSectorsTotalBPB / nSectorsPerCylinderBPB);
nSectorsPerTrack = nSectorsPerTrackBPB;
/*
* OK, great, the disk appears to contain a valid BPB. But so do XDF disk images, which are
* diskette images with tracks containing:
*
* 1 8Kb sector (equivalent of 16 512-byte sectors)
* 1 2Kb sector (equivalent of 4 512-byte sectors)
* 1 1Kb sector (equivalent of 2 512-byte sectors)
* 1 512-byte sector (equivalent of, um, 1 512-byte sector)
*
* for a total of the equivalent of 23 512-byte sectors, or 11776 (0x2E00) bytes per track.
* For an 80-track diskette with 2 sides, that works out to a total of 3680 512-byte sectors,
* or 1884160 bytes, or 1.84Mb, which is the exact size of the (only) XDF diskette images we
* currently support.
*
* Moreover, the first two tracks (ie, the first cylinder) contain only 19 sectors, rather than
* 23, but the XDF format still pads those tracks with 4 unused sectors.
*
* So, data for the first track contains 1 boot sector ending at 512 (0x200), 11 FAT sectors
* ending at 6144 (0x1800), and 7 "micro-disk" sectors ending at 9728 (0x2600). Then there's
* 4 sectors (probably of "garbage") that end at 11776 (0x2E00).
*
* Data for the second track contains 7 root directory sectors ending at 15360 (0x3C00), followed
* by disk data.
*
* For more details, check out this very helpful article: http://www.os2museum.com/wp/the-xdf-diskette-format/
*/
if (nSectorsTotalBPB == 3680) fXDF = true;
}
}
}
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),
* followed by 7 more bytes):
* 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), followed by 7 more bytes):
*
* 0x01: # heads (1 byte)
* 0x02: # cylinders (2 bytes)
* 0x04: # sectors/track (2 bytes)
* 0x06: # bytes/sector (2 bytes)
*
* which may be followed by an array of track table entries if the words at 0x04 and 0x06 are zero. If the track table exists, each
* entry contains the following:
* which may be followed by an array of track table entries if the words at 0x04 and 0x06 are zero.
* If the track table exists, each entry contains the following:
*
* 0x00: # sectors/track (2 bytes)
* 0x02: # bytes/sector (2 bytes)
* 0x04: file offset of track data (4 bytes)
*
* TODO: Our JSON disk format doesn't explicitly support a write-protect indicator. Instead, we include the string "write-protected"
* as a comment in the first line of the JSON data as a work-around. If the FDC component sees that comment string, it will honor it, but
* the FDC needs to provide its own user-controlled write-protection mechanism, too.
* TODO: Our JSON disk format doesn't explicitly support a write-protect indicator. Instead, we
* (used to) include the string "write-protected" as a comment in the first line of the JSON data
* as a work-around, and if the FDC component sees that comment string, it will honor it; however,
* we now prefer that read-only disk images simply include a "-readonly" suffix in the filename.
*/
if (!(bByte0 & 0xFE)) {
var cbSectorDSK = this.bufDisk.readUInt16LE(offBootSector + 0x06);
@ -2195,8 +2220,6 @@ DiskDump.prototype.convertToJSON = function()
}
for (var iCylinder=0; iCylinder < nCylinders; iCylinder++) {
// if (fDebug) console.log("dumping cylinder " + iCylinder);
var aHeads;
if (this.fJSONNative) {
aHeads = new Array(nHeads);
@ -2207,8 +2230,6 @@ 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];
@ -2219,8 +2240,6 @@ DiskDump.prototype.convertToJSON = function()
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);
@ -2229,24 +2248,30 @@ DiskDump.prototype.convertToJSON = function()
json += this.dumpLine(2, "[", "head:" + this.sJSONWhitespace + iHead + ", track:" + this.sJSONWhitespace + iCylinder);
}
for (var iSector=1, offSector=0; offSector < cbTrack; iSector++, offSector += cbSector) {
var cbSectorThisTrack = cbSector;
var nSectorsThisTrack = nSectorsPerTrack;
if (fXDF) nSectorsThisTrack = (iCylinder? 4 : 19);
for (var iSector=1, offSector=0; iSector <= nSectorsThisTrack && offSector < cbTrack; iSector++, offSector += cbSectorThisTrack) {
var sector = {};
// if (fDebug) console.log(" dumping sector " + iSector);
bufSector = bufTrack.slice(offSector, offSector + cbSector);
if (fXDF && iCylinder) cbSectorThisTrack = (iSector == 1? 8192 : (iSector == 2? 2048 : (iSector == 3? 1024 : 512)));
bufSector = bufTrack.slice(offSector, offSector + cbSectorThisTrack);
if (this.fJSONNative) {
sector['sector'] = iSector;
sector['length'] = cbSector;
sector['length'] = cbSectorThisTrack;
} else {
json += (iSector == 1? this.dumpLine(2, "{") : "");
json += this.dumpLine(0, '"sector":' + this.sJSONWhitespace + iSector + ",");
json += this.dumpLine(0, '"length":' + this.sJSONWhitespace + cbSector + ",");
json += this.dumpLine(0, '"length":' + this.sJSONWhitespace + cbSectorThisTrack + ",");
}
var aTrim = this.trimSector(bufSector, cbSector);
var aTrim = this.trimSector(bufSector, cbSectorThisTrack);
var dwPattern = aTrim[0];
var cbBuffer = cbSector;
var cbBuffer = cbSectorThisTrack;
if (dwPattern !== null) {
cbBuffer = aTrim[1];
if (this.fJSONNative) {
@ -2255,6 +2280,7 @@ DiskDump.prototype.convertToJSON = function()
json += this.dumpLine(0, '"pattern":' + this.sJSONWhitespace + dwPattern + ",");
}
}
if (this.fJSONNative) {
var dataSector = [];
sector['data'] = dataSector;
@ -2266,17 +2292,19 @@ DiskDump.prototype.convertToJSON = function()
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)
/*
* 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 < nSectorsPerTrack? this.dumpLine(0, "},{") : this.dumpLine(-2, "}"));
json += (iSector < nSectorsThisTrack? this.dumpLine(0, "},{") : this.dumpLine(-2, "}"));
}
}
if (!this.fJSONNative) json += this.dumpLine(-2, "]" + (iHead+1 == nHeads? "" : ",")); // end of head {iHead}, track {iCylinder}
offHead += cbTrack;
if (!this.fJSONNative) json += this.dumpLine(-2, "]" + (iHead+1 == nHeads? "" : ","));
offHead += cbTrack; // end of head {iHead}, track {iCylinder}
}
offTrack += offHead;
if (!this.fJSONNative) json += this.dumpLine(-2, "]" + (iCylinder+1 == nCylinders? "" : ",")); // end of cylinder {iCylinder}
if (!this.fJSONNative) json += this.dumpLine(-2, "]" + (iCylinder+1 == nCylinders? "" : ","));
offTrack += offHead; // end of cylinder {iCylinder}
}
/*
* Here's where we could output the following comment:

View file

@ -75,7 +75,6 @@ function FileDump(sFormat, fComments, fDecimal, sServerRoot)
/*
* Class constants
*/
FileDump.sAPIURL = "http://www.pcjs.org" + DumpAPI.ENDPOINT;
FileDump.sCopyright = "© 2012-2014 by Jeff Parsons (@jeffpar)";
FileDump.sNotice = FileDump.sAPIURL + " " + FileDump.sCopyright;

View file

@ -659,6 +659,7 @@ Disk.prototype.onLoadDisk = function(sDiskFile, sDiskData, nErrorCode, sDiskPath
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];
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...
length = sector['length'] = 512;
@ -1156,7 +1157,7 @@ Disk.prototype.seek = function(iCylinder, iHead, iSector, fWrite, done)
}
if (track) {
for (i = 0; i < track.length; i++) {
if (track[i]['sector'] == iSector) {
if (track[i] && track[i]['sector'] == iSector) {
/*
* If the sector's pattern is null, then this sector's true contents have not yet
* been fetched from the server.

View file

@ -1195,10 +1195,14 @@ FDC.prototype.mountDiskette = function(drive, disk, sDisketteName, sDiskettePath
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.
* drives cannot read DSHD (80-track) diskettes. However, I no longer require that the diskette's
* sectors/track fall within the drive's standard maximum, because XDF diskettes use 19 physical sectors/track
* on the first cylinder (1 more than the typical 18 sectors/track found on 1.44Mb diskettes) but declare
* a larger logical size (23 512-byte sectors/track) to reflect the actual capacity of XDF tracks beyond the
* first cylinder (ie, one 8Kb sector, one 2Kb sector, one 1Kb sector, and one 512-byte sector).
*/
aDiskInfo = disk.info();
if (disk && aDiskInfo[0] > drive.nCylinders || aDiskInfo[1] > drive.nHeads || aDiskInfo[2] > drive.nSectors) {
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;
}
@ -2255,7 +2259,7 @@ FDC.prototype.intBIOSDiskette = function(addr)
{
if (DEBUGGER) {
var DL = this.cpu.regDX & 0xff;
if (this.dbg && (this.dbg.messageEnabled(FDC.MESSAGE_FDC) || this.dbg.messageEnabled(FDC.MESSAGE_INT)) && DL < 0x80) {
if (this.dbg && this.dbg.messageEnabled(FDC.MESSAGE_FDC | FDC.MESSAGE_INT) && DL < 0x80) {
this.dbg.messageInt(FDC.BIOS.INT_DISKETTE, addr);
this.cpu.addIntReturn(addr, function(fdc, nCycles) {
return function onBIOSDisketteReturn(nLevel) {

View file

@ -1369,7 +1369,7 @@ HDC.prototype.inATCData = function(port, addrFrom)
if (this.drive) {
/*
* messagePort() calls, if enabled, can be too overwhelming for this port, so limit them to the first byte.
* messagePort() calls, if enabled, can be overwhelming for this port, so limit them to the first byte.
*/
fSuppress = (this.drive.ibSector > 0);
@ -1388,6 +1388,7 @@ HDC.prototype.inATCData = function(port, addrFrom)
*/
if (this.drive.ibSector == this.drive.cbSector) {
this.drive.nBytes -= this.drive.cbSector;
this.regSecCnt = (this.regSecCnt - 1) & 0xff;
/*
* TODO: If the WITH_ECC bit is set in the READ_DATA command, then we need to support "stuffing" 4
* additional bytes into the inATCData() stream. And we must first set DATA_REQ in the STATUS register.
@ -1398,7 +1399,7 @@ HDC.prototype.inATCData = function(port, addrFrom)
if (b >= 0) {
if (hdc.chipset) hdc.chipset.setIRR(ChipSet.IRQ.ATC);
/*
* I shouldn't have to set BUSY again, because it should still be set, no?
* I shouldn't have to set BUSY (or DATA_REQ) again, because it should still be set, no?
*/
Component.assert(!!(hdc.regStatus & HDC.ATC.STATUS.BUSY));
} else {
@ -1432,7 +1433,7 @@ HDC.prototype.inATCData = function(port, addrFrom)
HDC.prototype.outATCData = function(port, bOut, addrFrom)
{
/*
* messagePort() calls, if enabled, can be too overwhelming for this port, so limit them to the first byte.
* messagePort() calls, if enabled, can be overwhelming for this port, so limit them to the first byte.
*/
if (!this.drive || this.drive.ibSector == 0) this.messagePort(port, bOut, addrFrom, "DATA");
@ -1449,10 +1450,11 @@ HDC.prototype.outATCData = function(port, bOut, addrFrom)
}
else if (this.drive.ibSector == this.drive.cbSector) {
this.drive.nBytes -= this.drive.cbSector;
this.regSecCnt = (this.regSecCnt - 1) & 0xff;
if (this.chipset) this.chipset.setIRR(ChipSet.IRQ.ATC);
if (this.drive.nBytes >= this.drive.cbSector) {
/*
* I shouldn't have to set BUSY again, because it should still be set, no?
* I shouldn't have to set BUSY (or DATA_REQ) again, because it should still be set, no?
*/
Component.assert(!!(this.regStatus & HDC.ATC.STATUS.BUSY));
} else {
@ -1716,7 +1718,7 @@ HDC.prototype.doATCommand = function()
var nHead = this.regDrvHd & HDC.ATC.DRVHD.HEAD_MASK;
var nCylinder = this.regCylLo | ((this.regCylHi & HDC.ATC.CYLHI.MASK) << 8);
var nSector = this.regSecNum;
var nSectors = this.regSecCnt;
var nSectors = this.regSecCnt || 256;
this.drive = null;
this.regError = 0;
@ -1818,8 +1820,8 @@ HDC.prototype.doATCommand = function()
*
* WPREC: 0x4B
* SECCNT: 0x11 (for 17 sectors per track)
* CYL: 0x100 (256, uh, what?)
* SECNUM: 0x0C (12, uh, what?)
* CYL: 0x100 (256 -- uh, what?)
* SECNUM: 0x0C (12 -- uh, what?)
* DRVHD: 0xA3 (max head of 0x03, for 4 total heads)
*
* The importance of SECCNT (nSectors) and DRVHD (nHeads) is controlling how multi-sector operations
@ -2373,9 +2375,7 @@ HDC.prototype.readByte = function(drive, done, fAutoInc)
*/
if (done) {
var hdc = this;
drive.disk.seek(drive.wCylinder, drive.bHead, drive.bSector +
drive.bSectorBias, false, function (sector, fAsync)
{
drive.disk.seek(drive.wCylinder, drive.bHead, drive.bSector + drive.bSectorBias, false, function(sector, fAsync) {
var b = -1;
if ((drive.sector = sector)) {
drive.ibSector = 0;
@ -2577,7 +2577,7 @@ HDC.prototype.intBIOSDisk = function(addr)
var DL = this.cpu.regDX & 0xff;
if (!AH && DL > 0x80) this.iDriveAllowFail = DL - 0x80;
if (DEBUGGER) {
if (this.dbg && (this.dbg.messageEnabled(HDC.MESSAGE_HDC) || this.dbg.messageEnabled(HDC.MESSAGE_INT)) && DL >= 0x80) {
if (this.dbg && this.dbg.messageEnabled(HDC.MESSAGE_HDC | HDC.MESSAGE_INT) && DL >= 0x80) {
this.dbg.messageInt(HDC.BIOS.INT_DISK, addr);
this.cpu.addIntReturn(addr, function (hdc, nCycles) {
return function onBIOSDiskReturn(nLevel) {