HDC disk I/O finally works with both PC AT and DeskPro 386 ROMs
This commit is contained in:
parent
376e70dd20
commit
95cef85fa8
1 changed files with 116 additions and 93 deletions
|
|
@ -251,14 +251,14 @@ HDC.ATC = {
|
|||
},
|
||||
STATUS: { // this.regStatus (read-only; reading clears IRQ.ATC)
|
||||
PORT: 0x1F7,
|
||||
BUSY: 0x80, // if this is set, no other STATUS bits are valid
|
||||
READY: 0x40, // if this is set (along with the SEEK_OK bit), the drive is ready to read/write/seek again
|
||||
WFAULT: 0x20, // write fault
|
||||
SEEK_OK: 0x10, // seek operation complete
|
||||
DATA_REQ: 0x08, // indicates that "the sector buffer requires servicing during a Read or Write command. If either bit 7 (BUSY) or this bit is active, a command is being executed. Upon receipt of any command, this bit is reset."
|
||||
CORRECTED: 0x04,
|
||||
ERROR: 0x01, // set when the previous command ended in an error; one or more bits are set in the ERROR register (the next command to the controller resets the ERROR bit)
|
||||
INDEX: 0x02, // set once for every revolution of the disk
|
||||
ERROR: 0x01 // set when the previous command ended in an error; one or more bits are set in the ERROR register (the next command to the controller resets the ERROR bit)
|
||||
CORRECTED: 0x04,
|
||||
DATA_REQ: 0x08, // indicates that "the sector buffer requires servicing during a Read or Write command. If either bit 7 (BUSY) or this bit is active, a command is being executed. Upon receipt of any command, this bit is reset."
|
||||
SEEK_OK: 0x10, // seek operation complete
|
||||
WFAULT: 0x20, // write fault
|
||||
READY: 0x40, // if this is set (along with the SEEK_OK bit), the drive is ready to read/write/seek again
|
||||
BUSY: 0x80 // if this is set, no other STATUS bits are valid
|
||||
},
|
||||
COMMAND: { // this.regCommand (write-only)
|
||||
PORT: 0x1F7,
|
||||
|
|
@ -266,7 +266,7 @@ HDC.ATC = {
|
|||
READ_DATA: 0x20, // also supports NO_RETRIES and WITH_ECC
|
||||
WRITE_DATA: 0x30, // also supports NO_RETRIES and WITH_ECC
|
||||
READ_VERF: 0x40, // also supports NO_RETRIES
|
||||
FORMAT_TRK: 0x50,
|
||||
FORMAT_TRK: 0x50, // TODO
|
||||
SEEK: 0x70, // low nibble x 500us equal stepping rate (except for 0, which corresponds to 35us)
|
||||
DIAGNOSE: 0x90,
|
||||
SETPARMS: 0x91,
|
||||
|
|
@ -1395,7 +1395,7 @@ HDC.prototype.inATCData = function(port, addrFrom)
|
|||
* well, then the caller will get 0xff.
|
||||
*/
|
||||
var hdc = this;
|
||||
bIn = this.readData(this.drive, function(b, fAsync, obj, off) {
|
||||
bIn = this.readData(this.drive, function onATCReadData(b, fAsync, obj, off) {
|
||||
hdc.assert(!fAsync);
|
||||
if (BACKTRACK) {
|
||||
if (!off && obj.file && hdc.messageEnabled(Messages.DISK)) {
|
||||
|
|
@ -1413,25 +1413,23 @@ HDC.prototype.inATCData = function(port, addrFrom)
|
|||
});
|
||||
this.assert(bIn >= 0);
|
||||
|
||||
if (this.drive.ibSector == 1) {
|
||||
if (this.drive.ibSector == 1 || this.drive.ibSector == this.drive.cbSector) {
|
||||
/*
|
||||
* printMessageIO() calls, if enabled, can be overwhelming for this port, so limit them to the first byte
|
||||
* of each sector.
|
||||
* printMessageIO() calls, if enabled, can be overwhelming for this port, so limit them to the first
|
||||
* and last bytes of each sector.
|
||||
*/
|
||||
if (this.messageEnabled(Messages.PORT | Messages.HDC)) {
|
||||
this.printMessageIO(port, null, addrFrom, "DATA[" + this.drive.ibSector + "]", bIn);
|
||||
}
|
||||
}
|
||||
else if (this.drive.ibSector == this.drive.cbSector) {
|
||||
/*
|
||||
* Now that we've supplied a full sector of data, see if the caller's expecting additional sectors;
|
||||
* if so, prime the pump again. The caller should not poll us again until another interrupt's been delivered.
|
||||
*/
|
||||
if (this.drive.ibSector > 1) { // in other words, if this.drive.ibSector == this.drive.cbSector...
|
||||
if (this.messageEnabled(Messages.DATA | Messages.HDC)) {
|
||||
var sDump = this.drive.disk.dumpSector(this.drive.sector);
|
||||
if (sDump) this.dbg.message(sDump);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that we've supplied a full sector of data, see if the caller's expecting additional sectors;
|
||||
* if so, prime the pump again. The caller should not poll us again until another interrupt's delivered.
|
||||
*/
|
||||
this.drive.nBytes -= this.drive.cbSector;
|
||||
this.regSecCnt = (this.regSecCnt - 1) & 0xff;
|
||||
/*
|
||||
|
|
@ -1439,11 +1437,21 @@ HDC.prototype.inATCData = function(port, addrFrom)
|
|||
* additional bytes into the inATCData() stream. And we must first set DATA_REQ in the STATUS register.
|
||||
*/
|
||||
if (this.drive.nBytes >= this.drive.cbSector) {
|
||||
hdc.regStatus = HDC.ATC.STATUS.BUSY | HDC.ATC.STATUS.DATA_REQ;
|
||||
this.readData(this.drive, function(b, fAsync) {
|
||||
/*
|
||||
* FYI, with regard to regStatus, I'm simply aping what the ATC.COMMAND.READ_DATA setup code does
|
||||
* for the first sector, which may not strictly be necessary for subsequent sectors....
|
||||
*/
|
||||
hdc.regStatus = HDC.ATC.STATUS.BUSY;
|
||||
this.readData(this.drive, function onATCReadDataNext(b, fAsync) {
|
||||
if (b >= 0) {
|
||||
hdc.setATCIRR();
|
||||
hdc.regStatus = HDC.ATC.STATUS.READY | HDC.ATC.STATUS.SEEK_OK;
|
||||
/*
|
||||
* FYI, I'm taking a shotgun approach to these status bits: I need to clear STATUS.BUSY and
|
||||
* set STATUS.DATA_REQ, because otherwise CompaqDeskPro386 reads will fail, and I need to set
|
||||
* the STATUS.READY and STATUS.SEEK_OK bits, because otherwise MODEL_5170_REV3 reads will fail.
|
||||
*/
|
||||
hdc.regStatus = HDC.ATC.STATUS.READY | HDC.ATC.STATUS.SEEK_OK | HDC.ATC.STATUS.DATA_REQ;
|
||||
|
||||
} else {
|
||||
/*
|
||||
* TODO: It would be nice to be a bit more specific about the error (if any) that just occurred.
|
||||
|
|
@ -1460,6 +1468,7 @@ HDC.prototype.inATCData = function(port, addrFrom)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return bIn;
|
||||
};
|
||||
|
||||
|
|
@ -1486,22 +1495,19 @@ HDC.prototype.outATCData = function(port, bOut, addrFrom)
|
|||
this.printMessage("HDC.outATCData(" + str.toHexByte(bOut) + "): write failed");
|
||||
}
|
||||
}
|
||||
else if (this.drive.ibSector == 1) {
|
||||
else if (this.drive.ibSector == 1 || this.drive.ibSector == this.drive.cbSector) {
|
||||
/*
|
||||
* printMessageIO() calls, if enabled, can be overwhelming for this port, so limit them to the first byte
|
||||
* of each sector.
|
||||
* printMessageIO() calls, if enabled, can be overwhelming for this port, so limit them to the first
|
||||
* and last bytes of each sector.
|
||||
*/
|
||||
if (this.messageEnabled(Messages.PORT | Messages.HDC)) {
|
||||
this.printMessageIO(port, bOut, addrFrom, "DATA[" + this.drive.ibSector + "]");
|
||||
}
|
||||
}
|
||||
else if (this.drive.ibSector == this.drive.cbSector) {
|
||||
|
||||
if (this.drive.ibSector > 1) { // in other words, if this.drive.ibSector == this.drive.cbSector...
|
||||
if (this.messageEnabled(Messages.DATA | Messages.HDC)) {
|
||||
var sDump = this.drive.disk.dumpSector(this.drive.sector);
|
||||
if (sDump) this.dbg.message(sDump);
|
||||
}
|
||||
|
||||
this.drive.nBytes -= this.drive.cbSector;
|
||||
this.regSecCnt = (this.regSecCnt - 1) & 0xff;
|
||||
this.setATCIRR(true);
|
||||
|
|
@ -1512,6 +1518,7 @@ HDC.prototype.outATCData = function(port, bOut, addrFrom)
|
|||
this.assert(!this.drive.nBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* TODO: What to do about unexpected writes? The number of bytes has exceeded what the command specified.
|
||||
|
|
@ -1716,10 +1723,14 @@ HDC.prototype.outATCDrvHd = function(port, bOut, addrFrom)
|
|||
*
|
||||
* TODO: Dig into the ATC documentation some more, and determine what other situations, if any, regStatus
|
||||
* needs to be updated.
|
||||
*
|
||||
* UPDATE: The Compaq DeskPro 386 ROM BIOS requires setting STATUS.SEEK_OK in addition to STATUS.READY;
|
||||
* a quick retest of the MODEL_5170_REV3 BIOS suggests that it's happy with that change, so it's quite likely
|
||||
* that was the appropriate change all along.
|
||||
*/
|
||||
var iDrive = (this.regDrvHd & HDC.ATC.DRVHD.DRIVE_MASK? 1 : 0);
|
||||
if (this.aDrives[iDrive]) {
|
||||
this.regStatus |= HDC.ATC.STATUS.READY;
|
||||
this.regStatus |= HDC.ATC.STATUS.READY | HDC.ATC.STATUS.SEEK_OK;
|
||||
} else {
|
||||
this.regStatus &= ~HDC.ATC.STATUS.READY;
|
||||
}
|
||||
|
|
@ -1851,7 +1862,14 @@ HDC.prototype.doATC = function()
|
|||
|
||||
switch (bCmd & HDC.ATC.COMMAND.MASK) {
|
||||
|
||||
case HDC.ATC.COMMAND.READ_DATA:
|
||||
case HDC.ATC.COMMAND.RESTORE: // 0x10
|
||||
/*
|
||||
* Physically, this retracts the heads to cylinder 0, but logically, there isn't anything to do.
|
||||
*/
|
||||
fInterrupt = true;
|
||||
break;
|
||||
|
||||
case HDC.ATC.COMMAND.READ_DATA: // 0x20
|
||||
if (DEBUG && this.messageEnabled(Messages.HDC)) {
|
||||
this.printMessage("HDC.doRead(" + iDrive + ',' + drive.wCylinder + ':' + drive.bHead + ':' + drive.bSector + ',' + nSectors + ")", true);
|
||||
}
|
||||
|
|
@ -1859,16 +1877,19 @@ HDC.prototype.doATC = function()
|
|||
* We're using a call to readData() that disables auto-increment, so that once we've got the first
|
||||
* byte of the next sector, we can signal an interrupt without also consuming the first byte, allowing
|
||||
* inATCData() to begin with that byte.
|
||||
*
|
||||
* As with the WRITE_DATA command, I'm not sure which of BUSY and DATA_REQ (or both) should be set here,
|
||||
* so I'm setting both of them for now. I clear them as soon as I have data.
|
||||
*/
|
||||
hdc.regStatus = HDC.ATC.STATUS.BUSY | HDC.ATC.STATUS.DATA_REQ;
|
||||
|
||||
this.readData(drive, function(b, fAsync) {
|
||||
hdc.regStatus = HDC.ATC.STATUS.BUSY;
|
||||
this.readData(drive, function onATCReadDataFirst(b, fAsync) {
|
||||
if (b >= 0 && hdc.chipset) {
|
||||
hdc.setATCIRR();
|
||||
hdc.regStatus = HDC.ATC.STATUS.READY | HDC.ATC.STATUS.SEEK_OK;
|
||||
/*
|
||||
* Bytes from the requested sector(s) will now be delivered via inATCData().
|
||||
*
|
||||
* FYI, I'm taking a shotgun approach to these status bits: I need to clear STATUS.BUSY and
|
||||
* set STATUS.DATA_REQ, because otherwise CompaqDeskPro386 reads will fail, and I need to set
|
||||
* the STATUS.READY and STATUS.SEEK_OK bits, because otherwise MODEL_5170_REV3 reads will fail.
|
||||
*/
|
||||
hdc.regStatus = HDC.ATC.STATUS.READY | HDC.ATC.STATUS.SEEK_OK | HDC.ATC.STATUS.DATA_REQ;
|
||||
} else {
|
||||
/*
|
||||
* TODO: It would be nice to be a bit more specific about the error (if any) that just occurred.
|
||||
|
|
@ -1880,21 +1901,14 @@ HDC.prototype.doATC = function()
|
|||
}, false);
|
||||
break;
|
||||
|
||||
case HDC.ATC.COMMAND.WRITE_DATA:
|
||||
case HDC.ATC.COMMAND.WRITE_DATA: // 0x30
|
||||
if (DEBUG && this.messageEnabled(Messages.HDC)) {
|
||||
this.printMessage("HDC.doWrite(" + iDrive + ',' + drive.wCylinder + ':' + drive.bHead + ':' + drive.bSector + ',' + nSectors + ")", true);
|
||||
}
|
||||
this.regStatus = HDC.ATC.STATUS.DATA_REQ;
|
||||
break;
|
||||
|
||||
case HDC.ATC.COMMAND.RESTORE:
|
||||
/*
|
||||
* Physically, this retracts the heads to cylinder 0, but logically, there isn't anything to do.
|
||||
*/
|
||||
fInterrupt = true;
|
||||
break;
|
||||
|
||||
case HDC.ATC.COMMAND.READ_VERF:
|
||||
case HDC.ATC.COMMAND.READ_VERF: // 0x40
|
||||
/*
|
||||
* Since the READ VERIFY command returns no data, once again, logically, there isn't much we HAVE to
|
||||
* to do, but... TODO: Verify that all the disk parameters are valid, and return an error if they're not.
|
||||
|
|
@ -1902,12 +1916,21 @@ HDC.prototype.doATC = function()
|
|||
fInterrupt = true;
|
||||
break;
|
||||
|
||||
case HDC.ATC.COMMAND.DIAGNOSE:
|
||||
case HDC.ATC.COMMAND.SEEK: // 0x70
|
||||
/*
|
||||
* Physically, this moves the head(s) to the requested cylinder, but logically, there isn't anything to do;
|
||||
* in fact, we didn't even need this command for the MODEL_5170 ROM BIOS (the Compaq DeskPro 386 ROM BIOS was
|
||||
* another story).
|
||||
*/
|
||||
fInterrupt = true;
|
||||
break;
|
||||
|
||||
case HDC.ATC.COMMAND.DIAGNOSE: // 0x90
|
||||
this.regError = HDC.ATC.DIAG.NO_ERROR;
|
||||
fInterrupt = true;
|
||||
break;
|
||||
|
||||
case HDC.ATC.COMMAND.SETPARMS:
|
||||
case HDC.ATC.COMMAND.SETPARMS: // 0x91
|
||||
/*
|
||||
* The documentation implies that the only parameters this command really affects are the number
|
||||
* of heads (from regDrvHd) and sectors/track (from regSecCnt) -- this despite the fact that the BIOS
|
||||
|
|
@ -2109,7 +2132,7 @@ HDC.prototype.doXTC = function()
|
|||
break;
|
||||
|
||||
case HDC.XTC.DATA.CMD.READ_DATA: // 0x08
|
||||
this.doDMARead(drive, function(bStatus) {
|
||||
this.doDMARead(drive, function onXTCReadDataCommand(bStatus) {
|
||||
hdc.beginResult(bStatus | bDrive);
|
||||
});
|
||||
break;
|
||||
|
|
@ -2120,13 +2143,13 @@ HDC.prototype.doXTC = function()
|
|||
* but it is omitted from the HDC.XTC.DATA.CMD.READ_DATA command. Is that correct? Note that, as far as the length
|
||||
* of the transfer is concerned, we rely exclusively on the DMA controller being programmed with the appropriate byte count.
|
||||
*/
|
||||
this.doDMAWrite(drive, function(bStatus) {
|
||||
this.doDMAWrite(drive, function onXTCWriteDataCommand(bStatus) {
|
||||
hdc.beginResult(bStatus | bDrive);
|
||||
});
|
||||
break;
|
||||
|
||||
case HDC.XTC.DATA.CMD.WRITE_BUFFER: // 0x0F
|
||||
this.doDMAWriteBuffer(drive, function(bStatus) {
|
||||
this.doDMAWriteBuffer(drive, function onXTCWriteBufferCommand(bStatus) {
|
||||
hdc.beginResult(bStatus | bDrive);
|
||||
});
|
||||
break;
|
||||
|
|
@ -2304,7 +2327,7 @@ HDC.prototype.doDMARead = function(drive, done)
|
|||
*/
|
||||
drive.errorCode = HDC.XTC.DATA.ERR.NONE;
|
||||
this.chipset.connectDMA(ChipSet.DMA_HDC, this, 'dmaRead', drive);
|
||||
this.chipset.requestDMA(ChipSet.DMA_HDC, function(fComplete) {
|
||||
this.chipset.requestDMA(ChipSet.DMA_HDC, function onDMAReadRequest(fComplete) {
|
||||
if (!fComplete) {
|
||||
/*
|
||||
* If an incomplete request wasn't triggered by an explicit error, then let's make explicit
|
||||
|
|
@ -2348,7 +2371,7 @@ HDC.prototype.doDMAWrite = function(drive, done)
|
|||
*/
|
||||
drive.errorCode = HDC.XTC.DATA.ERR.NONE;
|
||||
this.chipset.connectDMA(ChipSet.DMA_HDC, this, 'dmaWrite', drive);
|
||||
this.chipset.requestDMA(ChipSet.DMA_HDC, function(fComplete) {
|
||||
this.chipset.requestDMA(ChipSet.DMA_HDC, function onDMAWriteRequest(fComplete) {
|
||||
if (!fComplete) {
|
||||
/*
|
||||
* If an incomplete request wasn't triggered by an explicit error, then let's make explicit
|
||||
|
|
@ -2399,7 +2422,7 @@ HDC.prototype.doDMAWriteBuffer = function(drive, done)
|
|||
*/
|
||||
drive.errorCode = HDC.XTC.DATA.ERR.NONE;
|
||||
this.chipset.connectDMA(ChipSet.DMA_HDC, this, 'dmaWriteBuffer', drive);
|
||||
this.chipset.requestDMA(ChipSet.DMA_HDC, function(fComplete) {
|
||||
this.chipset.requestDMA(ChipSet.DMA_HDC, function onDMAWriteBufferRequest(fComplete) {
|
||||
if (!fComplete) {
|
||||
/*
|
||||
* If an incomplete request wasn't triggered by an explicit error, then let's make explicit
|
||||
|
|
@ -2453,7 +2476,7 @@ HDC.prototype.doDMAFormat = function(drive, done)
|
|||
//
|
||||
drive.errorCode = HDC.XTC.DATA.ERR.NONE;
|
||||
this.chipset.connectDMA(ChipSet.DMA_HDC, this, 'dmaWriteFormat', drive);
|
||||
this.chipset.requestDMA(ChipSet.DMA_HDC, function(fComplete) {
|
||||
this.chipset.requestDMA(ChipSet.DMA_HDC, function onDMAFormat(fComplete) {
|
||||
if (!fComplete) {
|
||||
//
|
||||
// If an incomplete request wasn't triggered by an explicit error, then let's make explicit
|
||||
|
|
@ -2529,7 +2552,7 @@ HDC.prototype.readData = function(drive, done, fAutoInc)
|
|||
if (done) {
|
||||
var hdc = this;
|
||||
if (drive.disk) {
|
||||
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 onReadDataSeek(sector, fAsync) {
|
||||
if ((drive.sector = sector)) {
|
||||
obj = sector;
|
||||
off = drive.ibSector = 0;
|
||||
|
|
@ -2591,7 +2614,7 @@ HDC.prototype.writeData = function(drive, b)
|
|||
* but it seems preferable to keep the image format consistent and controller-independent.
|
||||
*/
|
||||
if (drive.disk) {
|
||||
drive.disk.seek(drive.wCylinder, drive.bHead, drive.bSector + drive.bSectorBias, true, function(sector, fAsync) {
|
||||
drive.disk.seek(drive.wCylinder, drive.bHead, drive.bSector + drive.bSectorBias, true, function onWriteDataSeek(sector, fAsync) {
|
||||
drive.sector = sector;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue