<> <> <> <<1. One and only one drive is handled by this Head. The Dandelion will>> <> <> <<2. The operation.buffer.length is ignored.>> <> DIRECTORY DeviceCleanup USING [Await, Item, Reason], DLionInputOutput USING [IOPage], Environment USING [Base], HeadStartChain USING [Start], Inline USING [BITAND, BITOR, DIVMOD, LongCOPY, LowHalf], SA800Face; SA800HeadDLion: PROGRAM IMPORTS DLionInputOutput, DeviceCleanup, RemainingHeads: HeadStartChain, Inline EXPORTS HeadStartChain, SA800Face SHARES SA800Face = BEGIN OPEN SA800Face; <<***********************************************************************>> <> <> <> <> <<***********************************************************************>> CSB: TYPE = MACHINE DEPENDENT RECORD [ transferMask(0): WORD, -- naked notify mask. next(1): IOPBlockPtr, -- IOCB executed when Output is done. unused0(2): UNSPECIFIED, needReset(3): BOOLEAN, -- TRUE => reset required. tail(4:0..31): IOCBPtr]; -- Last IOCB in queue. Used only by Initiate. <> csb: LONG POINTER TO CSB = LOOPHOLE[DLionInputOutput.IOPage + 111B]; -- 49'X <<***********************************************************************>> <> <<***********************************************************************>> IOCBPtr: TYPE = LONG POINTER TO IOCB; IOPBlockPtr: TYPE = Environment.Base RELATIVE POINTER TO IOPBlock; nilIOP: IOPBlockPtr = LOOPHOLE[0]; IOCB: TYPE = MACHINE DEPENDENT RECORD [ operation(0): Operation, -- set by head client iopBlk(8): IOPBlock, -- read/written by IOCB started(24:0..0): BOOLEAN, -- => IOCB has been submitted to IOP thisRun(24:1..15): [0..77777B], -- pages/tracks this trip abortStatus(25): Status, -- saved status. An operation may be aborted <> <> nextIOCB(26): IOCBPtr]; -- points to next IOCB in chain IOPBlock: TYPE = MACHINE DEPENDENT RECORD [ bufferAddr(0:0..31): LONG POINTER, -- address disk buffer (unused if format) unused(2): UNSPECIFIED _ 0, sectorLength(3): CARDINAL, -- sector length(bytes)(unused if format) sectorFormat(4): FormatWord, -- sector Format diskAddress(5:0..31): DiskAddress, -- cylinder, head and sector sectorCount(7): CARDINAL, -- sectors to transfer <= sectorPerTrack <<(if format, track count)>> result(8): IOPResult, -- status posted by IOP samePage(9:0..0): BOOLEAN, -- TRUE => use same memory page for <> command(9:1..15): Command, -- IOP command word subCommand(10): SubCommand, -- IOP sub-command word (IOP=esc Only) formatInfo(11:0..31): FormattingInfo, -- used only in format reserved(13:0..47): ARRAY [0..3) OF UNSPECIFIED]; FormattingInfo: TYPE = MACHINE DEPENDENT RECORD [ sectorLengthDiv4(0:0..7): CARDINAL [0..400B), -- sector length <<((bytes)/4) MOD 256>> encodedSectorLength(0:8..15): EncodedSectorLength, -- encoded sector length sectorsPerTrack(1:0..7): CARDINAL [0..400B), gap3FillCharsCount(1:8..15): CARDINAL [0..400B)]; -- interrecord gap count <> Command: TYPE = MACHINE DEPENDENT { nopC(0), readSectorC, writeSectorC, writeDeletedSectorC, readIDC, formatTrackC, recalibrateC, initializeC, escapeC, (77777B)}; SubCommand: TYPE = MACHINE DEPENDENT {nopSC(0), diskChangeClearSC, (177777B)}; EncodedSectorLength: TYPE = MACHINE DEPENDENT { b128(0), b256(1), b512(2), b1024(3), bUnknown}; FormatWord: TYPE = MACHINE DEPENDENT RECORD [ troyOrIBM(0:0..11): MACHINE DEPENDENT {IBM(0), Troy(1), (7777B)}, density(0:12..15): MACHINE DEPENDENT {single(0), double(10B), (17B)}]; IOPResult: TYPE = MACHINE DEPENDENT RECORD [ doorOpened(0:0..0): BOOLEAN, -- TRUE => operator opened disk door <> <> unused1(0:1..1): BOOLEAN _ FALSE, twoSided(0:2..2): BOOLEAN, -- true => two sided diskette installed diskID(0:3..3): MACHINE DEPENDENT {SA800(0), SA850(1)}, error(0:4..4): BOOLEAN, -- IOP detected an error in the IOCB unused2(0:5..5): BOOLEAN _ FALSE, recalibrateError(0:6..6): BOOLEAN, -- IOP had error during recalibrate op dataLost(0:7..7): BOOLEAN, -- data transfer not completed notReady(0:8..8): BOOLEAN, -- drive has door open writeProtect(0:9..9): BOOLEAN, -- write-protected diskette now in drive deletedData(0:10..10): BOOLEAN, -- read a deleted data sector recordNotFound(0:11..11): BOOLEAN, -- some sector in transfer not found crcError(0:12..12): BOOLEAN, -- CRC error on read op track00(0:13..13): BOOLEAN, -- read heads left on cylinder 0 unused3(0:14..14): BOOLEAN _ FALSE, busy(0:15..15): BOOLEAN]; -- => chip busy, should always be FALSE <<***********************************************************************>> <> <<(canned stati can be found at the end of the module (so they won't lexically>> <> <<***********************************************************************>> commandMatrix: ARRAY Function OF Command = [ -- face => IOP command nopC, recalibrateC, escapeC, readSectorC, writeSectorC, writeDeletedSectorC, readIDC, formatTrackC]; nullIOCB: IOCB = [ operation: [ device: LOOPHOLE[0], function: nop, incrementDataPointer: FALSE, count: 0, address: [cylinder: 0, head: 0, sector: 0], buffer: [address: NIL, length: 0]], iopBlk: nullIOPBlk, started: FALSE, thisRun: 0, abortStatus: initialStatus, nextIOCB: NIL]; nullIOPBlk: IOPBlock =[ bufferAddr: NIL, unused: 0, sectorLength: 0, sectorFormat: [troyOrIBM: IBM, density: single], diskAddress: [cylinder: 0, head: 0, sector: 0], sectorCount: 0, result: nullIOPResult, samePage: FALSE, command: nopC, subCommand: nopSC, formatInfo: [0, b128, 0, 0], reserved: [0, 0, 0]]; nullIOPResult: IOPResult = [ doorOpened: FALSE, twoSided: FALSE, diskID: SA800, error: FALSE, recalibrateError: FALSE, dataLost: FALSE, notReady: FALSE, writeProtect: FALSE, deletedData: FALSE, recordNotFound: FALSE, crcError: FALSE, track00: FALSE, busy: FALSE]; nullDeviceHandle: PUBLIC DeviceHandle _ LOOPHOLE[0]; diskDeviceHandle: DeviceHandle = LOOPHOLE[1]; internalDeviceHandle: DeviceHandle = LOOPHOLE[2]; -- for internally generated IOCBs. trackParameterLength: CARDINAL = 0; -- space needed for FormatTrack parameters idLength: CARDINAL = 3; -- length of field ID in words numOfCylinders: CARDINAL = 77; -- number of cylinders on SA800 and SA850 disks initialAllocationLength: PUBLIC CARDINAL _ SIZE[IOCB]; operationBlockLength: PUBLIC CARDINAL _ SIZE[IOCB]; <<***********************************************************************>> <> <<***********************************************************************>> initializationIOCB: IOCBPtr; state: RECORD [ user: Context --user accessable state _ [protect: FALSE, format: IBM, density: double, sectorLength: 256], status: Status _ diskChange, --most currently processed status result: IOPResult _ nullIOPResult, --most currently processed IOP result sectorsPerTrack: CARDINAL _ 15, --function[sectorLength, density] encodedSectorLength: EncodedSectorLength _ b512];--function[sectorLength] <> <> <> <> <> DiskChangeClear: PUBLIC PROCEDURE [DeviceHandle] = BEGIN ready: BOOLEAN; IF ~state.status.diskChange THEN RETURN; state.status.diskChange _ FALSE; ready _ ~Immediate[escapeC, diskChangeClearSC, nop].notReady; <> <> < door lock engaged. If there were no disk, the FDC would>> <> <> <> IF ready THEN [] _ Immediate[recalibrateC, nopSC, recalibrate] END; <> <> GetDeviceAttributes: PUBLIC PROC [DeviceHandle] RETURNS [Attributes] = BEGIN RETURN[[ deviceType: IF state.result.diskID=SA800 THEN SA800 ELSE SA850, numberOfHeads: IF ~state.status.twoSided THEN 1 ELSE 2, numberOfCylinders: numOfCylinders, trackLength: trackParameterLength]]; END; <> GetNextDevice: PUBLIC PROC [device: DeviceHandle] RETURNS [DeviceHandle] = { RETURN[IF device = nullDeviceHandle THEN diskDeviceHandle ELSE nullDeviceHandle]}; GetContext: PUBLIC PROC [DeviceHandle] RETURNS [Context] = {RETURN[state.user]}; <> Initialize: PUBLIC PROC [notify: WORD, initialAllocation: LONG POINTER] = BEGIN csb^ _ [ transferMask: notify, next: nilIOP, unused0: NULL, needReset: FALSE, tail: NIL]; initializationIOCB _ initialAllocation; initializationIOCB^ _ nullIOCB; initializationIOCB.operation.device _ internalDeviceHandle; [] _ Immediate[initializeC, nopSC, nop]; state.status.diskChange _ TRUE; END; InitializeCleanup: PUBLIC PROC [DeviceHandle] = BEGIN item: DeviceCleanup.Item; sCsb: CSB; DO reason: DeviceCleanup.Reason = DeviceCleanup.Await[@item]; SELECT reason FROM turnOff, kill => {WHILE csb.next#nilIOP DO ENDLOOP; sCsb _ csb^}; turnOn => csb^ _ sCsb; ENDCASE ENDLOOP END; <> Initiate: PUBLIC PROC [o: OperationPtr] RETURNS [status: Status, maxSecondsThisOperation: CARDINAL] = BEGIN OPEN iocb: LOOPHOLE[o, IOCBPtr]; maxSecondsThisOperation _ 0; IF o.device # diskDeviceHandle AND o.device # internalDeviceHandle THEN RETURN[Inline.BITOR[statusError, Inline.BITAND[infoMask, state.status]], 0]; IF o.device = diskDeviceHandle THEN iocb.iopBlk _ nullIOPBlk; iocb.nextIOCB _ NIL; iocb.started _ FALSE; iocb.abortStatus _ inProgress; IF (status _ CheckIOCB[@iocb]).inProgress THEN BEGIN IF csb.next = nilIOP THEN RunIOCB[@iocb] ELSE csb.tail.nextIOCB _ @iocb; csb.tail _ @iocb; maxSecondsThisOperation _ SELECT o.function FROM readSector, writeSector, writeDeletedSector, readID => 20 + (o.count + iocb.iopBlk.sectorCount)/5, formatTrack => 20 + (o.count + iocb.iopBlk.sectorCount), nop, recalibrate => 10, ENDCASE => 0; END ELSE iocb.started _ TRUE; -- return (error) status if somebody polls this guy END; Poll: PUBLIC PROC [o: OperationPtr] RETURNS [status: Status] = BEGIN OPEN iocb: LOOPHOLE[o, IOCBPtr]; IF ~iocb.abortStatus.inProgress THEN RETURN[iocb.abortStatus]; IF csb.next#nilIOP OR ~iocb.started THEN RETURN[Inline.BITOR[inProgress, Inline.BITAND[state.status, infoMask]]]; IF (status _ GetStatus[@iocb]).inProgress THEN RunIOCB[@iocb] ELSE FOR newIOCB: IOCBPtr _ iocb.nextIOCB, newIOCB.nextIOCB WHILE newIOCB # NIL DO newIOCB.started _ TRUE; IF CheckIOCB[newIOCB].inProgress THEN {RunIOCB[newIOCB]; EXIT}; ENDLOOP; END; Reset: PUBLIC PROC [DeviceHandle] = {csb.next _ nilIOP; csb.needReset _ TRUE; csb.tail _ NIL}; <> <> <> SetContext: PUBLIC PROC [device: DeviceHandle, context: Context] RETURNS [BOOLEAN] = BEGIN Density: TYPE = {single, double}; sectorsPerTrack: ARRAY EncodedSectorLength[b128..b1024] OF ARRAY Density OF CARDINAL = [ -- Single density Double Density -- 64w-- [26, 36], -- 128w-- [15, 26], -- 256w-- [ 8, 15], -- 512w-- [ 4, 8]]; byteIndex: EncodedSectorLength = SELECT context.sectorLength FROM 64 => b128, 128 => b256, 256 => b512, 512 => b1024, ENDCASE => bUnknown; IF (byteIndex=bUnknown) OR (csb.next#nilIOP) OR (context.format#IBM) THEN RETURN[FALSE]; state.user _ context; state.sectorsPerTrack _ sectorsPerTrack[byteIndex][IF context.density=single THEN single ELSE double]; state.encodedSectorLength _ byteIndex; RETURN[TRUE]; END; <> Start: PUBLIC PROC = {RemainingHeads.Start[]}; <<************************** PRIVATE PROCEDURES **************************>> BuildIOCB: PROCEDURE [iocb: IOCBPtr] = BEGIN OPEN o: iocb.operation; b: LONG POINTER TO IOPBlock = @iocb.iopBlk; runLength: CARDINAL; IF o.device = internalDeviceHandle THEN RETURN; b.result _ nullIOPResult; IF state.status.notReady THEN {b.command _ nopC; RETURN}; b.command _ commandMatrix[o.function]; SELECT o.function FROM nop, recalibrate => NULL; -- allSetUp recovery => b.command _ nopC; -- for now, do nothing readSector, writeSector, writeDeletedSector, readID => BEGIN OPEN a: o.address; runLength _ MIN[o.count, state.sectorsPerTrack-(a.sector-1)]; Touch[ addr: o.buffer.address, count: (IF o.function=readID THEN idLength ELSE state.user.sectorLength) * (IF o.incrementDataPointer THEN runLength ELSE 1), dirty: (o.function=readSector OR o.function=readID)]; b.samePage _ ~o.incrementDataPointer; b.bufferAddr _ o.buffer.address; b.sectorLength _ state.user.sectorLength; b.sectorFormat _ [ troyOrIBM: IF state.user.format = Troy THEN Troy ELSE IBM, density: IF state.user.density = double THEN double ELSE single]; b.diskAddress _ o.address; -- set first disk address b.sectorCount _ runLength; -- set number of sectors to transfer iocb.thisRun _ runLength; -- and set run length for getStatus fixup. END; formatTrack => BEGIN OPEN a: o.address; Density: TYPE = {single, double}; gap3: ARRAY EncodedSectorLength [b128..b1024] OF ARRAY Density OF CARDINAL <<[512w][single] and [64w][double] are picked out of the air>> = [ -- Single density Double Density -- 64w-- [27, 26], -- 128w-- [42, 54], -- 256w-- [58, 84], -- 512w-- [75, 116]]; b.formatInfo _ [ sectorLengthDiv4: state.user.sectorLength/2, -- (x * 2 bytes/wd)/4 encodedSectorLength: state.encodedSectorLength, sectorsPerTrack: state.sectorsPerTrack, gap3FillCharsCount: gap3[state.encodedSectorLength] [IF state.user.density=single THEN single ELSE double]]; runLength _ MIN[ o.count, IF state.status.twoSided THEN 1 ELSE (numOfCylinders - a.cylinder)]; b.sectorFormat _ [ troyOrIBM: IF state.user.format = Troy THEN Troy ELSE IBM, density: IF state.user.density = double THEN double ELSE single]; b.diskAddress _ o.address; b.sectorCount _ runLength; iocb.thisRun _ runLength; END ENDCASE; END; <> <> CheckIOCB: PROCEDURE [iocb: IOCBPtr] RETURNS [status: Status] = BEGIN OPEN a: iocb.operation.address, o: iocb.operation, Inline; IsWriteOp: PROC [f: Function] RETURNS [BOOLEAN] = INLINE {RETURN[f=writeSector OR f=writeDeletedSector OR f=formatTrack]}; IF ~iocb.abortStatus.inProgress THEN RETURN[iocb.abortStatus]; status _ inProgress; IF o.function = formatTrack THEN a.sector _ 1; SELECT TRUE FROM o.device = internalDeviceHandle => NULL; state.status.diskChange => status _ diskChange; (IsWriteOp[o.function] AND (state.user.protect OR state.status.writeProtect)) => status _ writeProtect; (IsTransferOp[o.function] AND (a.cylinder >= numOfCylinders OR a.head > (IF state.status.twoSided THEN 1 ELSE 0) OR a.sector NOT IN [1..state.sectorsPerTrack] OR o.count = 0)) => status _ recordNotFound; ENDCASE => NULL; iocb.abortStatus _ status _ BITOR[status, BITAND[infoMask, state.status]]; END; <> <> <> <> <> GetStatus: PROCEDURE [iocb: IOCBPtr] RETURNS [status: Status] = BEGIN OPEN b: iocb.iopBlk, o: iocb.operation; command: Command = b.command; result: IOPResult _ b.result; -- done merely for better code pResult: POINTER TO READONLY IOPResult = @result; <> BEGIN OPEN a: o.address; countDone: CARDINAL _ (iocb.thisRun - b.sectorCount); o.count _ o.count - countDone; SELECT command FROM formatTrackC => IF countDone#0 THEN SELECT TRUE FROM <> b.result.twoSided AND (a.head = 0) => a.head _ 1; ENDCASE => {a.head _ 0; a.cylinder _ a.cylinder + countDone}; readSectorC, readIDC, writeSectorC, writeDeletedSectorC => BEGIN IF o.incrementDataPointer THEN BEGIN length: CARDINAL = (IF command=readIDC THEN idLength ELSE state.user.sectorLength) * countDone; o.buffer.length _ o.buffer.length - length; o.buffer.address _ o.buffer.address + length; END; [quotient: countDone, remainder: a.sector] _ Inline.DIVMOD[(a.sector-1) + countDone, state.sectorsPerTrack]; a.sector _ a.sector + 1; <> IF countDone#0 THEN SELECT TRUE FROM b.result.twoSided AND (a.head = 0) => a.head _ 1; ENDCASE => {a.head _ 0; a.cylinder _ a.cylinder + countDone}; END; ENDCASE; END; status _ initialStatus; -- _ ALL[FALSE]; status.diskChange _ state.status.diskChange OR (state.status.notReady AND ~pResult.notReady) OR (~state.status.notReady AND (pResult.notReady OR pResult.doorOpened)); status.twoSided _ pResult.twoSided; <> <<(_ TRUE) SA800 since Mokelumne pilot thinks this is a hardware error bit>> status.notReady _ pResult.notReady; status.writeProtect _ state.user.protect OR pResult.writeProtect; <> <> SELECT command FROM readSectorC => BEGIN status.deletedData _ pResult.deletedData; status.crcError _ pResult.crcError; status.dataLost _ pResult.dataLost; status.recordNotFound _ pResult.recordNotFound OR (o.address.cylinder >= numOfCylinders AND o.count > 0); END; IN [writeSectorC..readIDC] => BEGIN status.crcError _ pResult.crcError; status.dataLost _ pResult.dataLost; status.recordNotFound _ pResult.recordNotFound OR (o.address.cylinder >= numOfCylinders AND o.count > 0); END; formatTrackC => BEGIN status.dataLost _ pResult.dataLost; status.recordNotFound _ pResult.recordNotFound OR (o.address.cylinder >= numOfCylinders AND o.count > 0); END; recalibrateC => BEGIN status.recalibrateError _ pResult.recalibrateError; status.track00 _ pResult.track00; END; initializeC, escapeC => status.track00 _ pResult.track00; ENDCASE; <> status.error _ Inline.BITAND[status, statusError] # 0; status.inProgress _ ~status.error AND IsTransferOp[o.function] AND o.count > 0; status.busy _ status.inProgress OR (iocb.nextIOCB # NIL); state.status _ iocb.abortStatus _ status; END; Immediate: PROCEDURE [ command: Command, subCommand: SubCommand, function: Function] RETURNS [initStatus: Status] = BEGIN initializationIOCB.iopBlk.command _ command; initializationIOCB.iopBlk.subCommand _ subCommand; initializationIOCB.operation.function _ function; initializationIOCB.operation.device _ internalDeviceHandle; initStatus _ Initiate[@initializationIOCB.operation].status; WHILE initStatus.inProgress DO initStatus _ Poll[@initializationIOCB.operation] ENDLOOP END; IsTransferOp: PROC [f: Function] RETURNS [b: BOOLEAN] = --INLINE-- BEGIN b _ SELECT f FROM readSector, readID, writeSector, writeDeletedSector, formatTrack => TRUE, ENDCASE => FALSE; END; RunIOCB: PROC [iocb: IOCBPtr] = BEGIN BuildIOCB[iocb]; iocb.started _ TRUE; IF csb.next=nilIOP THEN csb.next _ LOOPHOLE[Inline.LowHalf[@iocb.iopBlk]]; END; <> Touch: PROC [addr: LONG POINTER, count: CARDINAL, dirty: BOOLEAN] = BEGIN IF dirty THEN BEGIN next: LONG POINTER = addr+1; IF count#0 THEN {addr^ _ 0; Inline.LongCOPY[from:addr, to: next, nwords: count-1]} END ELSE THROUGH [0..count) DO dummy: CARDINAL _ addr^; addr _ addr+1 ENDLOOP; END; <<***********************************************************************>> <> <<***********************************************************************>> recordNotFound: Status = [diskChange: FALSE, na1: FALSE, twoSided: FALSE, na3: FALSE, error: TRUE, inProgress: FALSE, recalibrateError: FALSE, dataLost: FALSE, notReady: FALSE, writeProtect: FALSE, deletedData: FALSE, recordNotFound: TRUE, crcError: FALSE, track00: FALSE, index: FALSE, busy: FALSE]; notReady: Status = [diskChange: FALSE, na1: FALSE, twoSided: FALSE, na3: FALSE, error: TRUE, inProgress: FALSE, recalibrateError: FALSE, dataLost: FALSE, notReady: TRUE, writeProtect: FALSE, deletedData: FALSE, recordNotFound: FALSE, crcError: FALSE, track00: FALSE, index: FALSE, busy: FALSE]; diskChange: Status = [diskChange: TRUE, na1: FALSE, twoSided: FALSE, na3: FALSE, error: TRUE, inProgress: FALSE, recalibrateError: FALSE, dataLost: FALSE, notReady: FALSE, writeProtect: FALSE, deletedData: FALSE, recordNotFound: FALSE, crcError: FALSE, track00: FALSE, index: FALSE, busy: FALSE]; initialStatus: Status = LOOPHOLE[0]; -- ALL[FALSE] inProgress: Status = [diskChange: FALSE, na1: FALSE, twoSided: FALSE, na3: FALSE, error: FALSE, inProgress: TRUE, recalibrateError: FALSE, dataLost: FALSE, notReady: FALSE, writeProtect: FALSE, deletedData: FALSE, recordNotFound: FALSE, crcError: FALSE, track00: FALSE, index: FALSE, busy: TRUE]; writeProtect: Status = [diskChange: FALSE, na1: FALSE, twoSided: FALSE, na3: FALSE, error: TRUE, inProgress: FALSE, recalibrateError: FALSE, dataLost: FALSE, notReady: FALSE, writeProtect: TRUE, deletedData: FALSE, recordNotFound: FALSE, crcError: FALSE, track00: FALSE, index: FALSE, busy: FALSE]; statusError: Status = [diskChange: TRUE, na1: FALSE, twoSided: FALSE, na3: FALSE, error: TRUE, inProgress: FALSE, recalibrateError: TRUE, dataLost: TRUE, notReady: TRUE, writeProtect: FALSE, deletedData: FALSE, recordNotFound: TRUE, crcError: TRUE, track00: FALSE, index: FALSE, busy: FALSE]; infoMask: Status = [diskChange: FALSE, na1: FALSE, twoSided: TRUE, na3: FALSE, error: FALSE, inProgress: FALSE, recalibrateError: FALSE, dataLost: FALSE, notReady: FALSE, writeProtect: TRUE, deletedData: FALSE, recordNotFound: FALSE, crcError: FALSE, track00: TRUE, index: FALSE, busy: FALSE]; END..... LOG Time: September 26, 1980 3:30 PM, by DDavies, start coding. Time: October 21, 1980 2:25 PM, add mapping of all pages for virt addresses Time: October 30, 1980 5:17 PM, add mapping of IOCB addresses Time: November 7, 1980 1:00 PM, add byte swapping for IDs read Time: November 12, 1980 11:13 AM, by DDavies, add bulletproofing against polling out of order. Time: December 1, 1980 1:35 PM, by DDavies, remove byte-swapping. Time: December 1, 1980 10:36 PM, by Forrest, run through reformatter. Time: January 22, 1981 11:10 AM, by Gobbel, fix bug that causes head to always die if function=formatTrack. Time: March 23, 1981 7:06 PM, by Forrest, fix bug causing recordNotFound when recalibrate/recover with count=0. (1792)\f8