DIRECTORY Basics USING [BITAND, BITOR, DivMod, LowHalf], DeviceCleanup USING [Await, Item, Reason], DeviceTypes USING [sa800, sa850], DLionInputOutput USING [floppyCSBOffset, floppyCSBMax, floppyIOCBAddressOffset, floppyIOCBAddressMax, IOPage], FloppyDiskFace, PrincOps USING [Alignment, Base], PrincOpsUtils USING [LongCopy]; FloppyHeadDLion: PROGRAM IMPORTS Basics, DeviceCleanup, DLionInputOutput, PrincOpsUtils EXPORTS FloppyDiskFace SHARES FloppyDiskFace = BEGIN OPEN FloppyDiskFace; CSB: TYPE = RECORD [transferMask: WORD]; -- naked notify mask. csb: LONG POINTER TO CSB = LOOPHOLE[DLionInputOutput.IOPage + DLionInputOutput.floppyCSBOffset]; compileCheckFloppyCSBSize: BOOLEAN[TRUE..TRUE] = (DLionInputOutput.floppyCSBMax >= SIZE[CSB]); nextIOCBAddress: LONG POINTER TO IOPBlockPtr = LOOPHOLE[DLionInputOutput.IOPage + DLionInputOutput.floppyIOCBAddressOffset]; compileCheckNextIOCBAddressSize: BOOLEAN[TRUE..TRUE] = (DLionInputOutput.floppyIOCBAddressMax >= SIZE[IOPBlockPtr]); IOCBPtr: TYPE = LONG POINTER TO IOCB; IOPBlockPtr: TYPE = PrincOps.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): DetailedStatus, -- saved status. An operation may be aborted after it passes Initiate (new Disk Change while it is in the queue). This saves the status so the user finds out why it failed. nextIOCB(26): IOCBPtr, -- points to next IOCB in chain triesDone(28): CARDINAL, -- crc/dataLost/index retries Done recNotFoundTries(29): CARDINAL, -- record not found retries count totalRetries(30): CARDINAL, -- total of all retries done writeProtection(31): BOOLEAN]; -- Context WriteProtection 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 all sectors (unused if format) 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 latched until DiskChangeClear[] AND ~notReady 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 DetailedStatus: TYPE = MACHINE DEPENDENT RECORD [ diskChange: BOOLEAN, --Disk drive has apparently gone to a not ready (door open) state since the last operation was performed. Only DiskChangeClear will reset this status. na1: BOOLEAN, --UNUSED twoSided: BOOLEAN, --The diskette currently installed is two-sided. na3: BOOLEAN, --UNUSED error: BOOLEAN, --This status record indicates an error inProgress: BOOLEAN, --The operation is not yet complete recalibrateError: BOOL, --recalibrate subfunction failed to sense track00. dataLost: BOOLEAN, -- data transfer requested was larger than expected. notReady: BOOLEAN, -- The drive is not ready. writeProtect: BOOLEAN, --Logical OR of the context setting of protect and the physical signal being returned from the drive. deletedData: BOOLEAN, --The sector contained a deleted data address mark. recordNotFound: BOOL, --The record defined by the disk address could not be found. crcError: BOOLEAN, --A CRC error was encountered on either the record ID or data field. track00: BOOLEAN, --Read/Write heads are currently positioned over track00, the outermost track. index: BOOLEAN, --An index pulse signal was detected concurrent with the posting of this status. busy: BOOLEAN]; --The drive is not idle. commandMatrix: ARRAY Function OF Command = [ -- face => IOP command nopC, readSectorC, writeSectorC, writeDeletedSectorC, readIDC, formatTrackC]; nullIOCB: IOCB = [ operation: [device: LOOPHOLE[0], function: nop, incrementDataPointer: FALSE, tries: 1, count: 0, address: [cylinder: 0, head: 0, sector: 0], dataPtr: NIL], iopBlk: nullIOPBlk, started: FALSE, thisRun: 0, abortStatus: initialStatus, nextIOCB: NIL, triesDone: 0, recNotFoundTries: 0, totalRetries: 0, writeProtection: FALSE]; 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]; operationAlignment: PUBLIC PrincOps.Alignment _ a4; maxRecNotFoundRetries: CARDINAL = 3; maxRetries: CARDINAL = 30; retryLimit: CARDINAL = 10; standardRetries: Tries = 200B; noRetries: Tries = 1B; initializationIOCB: IOCBPtr; state: RECORD [ user: Context --user accessable state _ [protect: FALSE, format: IBM, density: double, sectorLength: 256], status: DetailedStatus _ diskChange, --most currently processed status result: IOPResult _ nullIOPResult, --most currently processed IOP result sectorsPerTrack: CARDINAL _ 15, --function[sectorLength, density] encodedSectorLength: EncodedSectorLength _ b512]; --function[sectorLength] tail: IOCBPtr; iocbContext: Context _ [protect: FALSE, format: IBM, density: double, sectorLength: 256]; DiskChangeClear: PUBLIC PROCEDURE [DeviceHandle] = BEGIN ready: BOOLEAN; IF ~state.status.diskChange THEN RETURN; state.status.diskChange _ FALSE; ready _ ~(Immediate[escapeC, diskChangeClearSC, TRUE] = notReady); IF ready THEN [] _ Immediate[recalibrateC, nopSC, FALSE] END; GetDeviceAttributes: PUBLIC PROC [DeviceHandle] RETURNS [Attributes] = BEGIN RETURN[[ type: IF state.result.diskID = SA800 THEN DeviceTypes.sa800 ELSE DeviceTypes.sa850, --add later numberOfHeads: IF ~state.status.twoSided THEN 1 ELSE 2, numberOfCylinders: numOfCylinders, maxSectorsPerTrack: LOOPHOLE[MaxSectorsPerTrack[state.user].maxSectorsPerTrack], formatLength: trackParameterLength, ready: IF state.status.notReady THEN FALSE ELSE TRUE, diskChange: state.status.diskChange, twoSided: state.status.twoSided, busy: state.status.busy ]]; 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]; nextIOCBAddress^ _ nilIOP; tail _ NIL; initializationIOCB _ initialAllocation; initializationIOCB^ _ nullIOCB; initializationIOCB.operation.device _ internalDeviceHandle; [] _ Immediate[initializeC, nopSC, TRUE]; state.status.diskChange _ TRUE; END; InitializeCleanup: PUBLIC PROC = BEGIN item: DeviceCleanup.Item; nextIOCBAddr: IOPBlockPtr; sCsb: CSB; DO reason: DeviceCleanup.Reason = DeviceCleanup.Await[@item]; SELECT reason FROM turnOff, kill => { WHILE nextIOCBAddress^ # nilIOP DO ENDLOOP; sCsb _ csb^; nextIOCBAddr _ nextIOCBAddress^}; turnOn => { csb^ _ sCsb; nextIOCBAddress^ _ nextIOCBAddr}; ENDCASE ENDLOOP END; Initiate: PUBLIC PROC [o: OperationPtr] RETURNS [status: Status] = { OPEN iocb: LOOPHOLE[o, IOCBPtr]; IF o.device # diskDeviceHandle AND o.device # internalDeviceHandle THEN RETURN[GetStatusForChannel[LOOPHOLE[Basics.BITOR[LOOPHOLE[statusError], Basics.BITAND[LOOPHOLE[infoMask], LOOPHOLE[state.status]]]]]]; IF o.device = diskDeviceHandle THEN iocb.iopBlk _ nullIOPBlk; iocb.nextIOCB _ NIL; iocb.started _ FALSE; iocb.abortStatus _ inProgress; iocb.triesDone _ 0; iocb.recNotFoundTries _ 0; iocb.totalRetries _ 0; iocbContext _ state.user; IF (status _ GetStatusForChannel[CheckIOCB[@iocb]]) = inProgress THEN { IF nextIOCBAddress^ = nilIOP THEN RunIOCB[@iocb] --empty queue ELSE {SetupIOCBContext[@iocb]; tail.nextIOCB _ @iocb}; --queue not empty tail _ @iocb; --last iocb address set to this iocb } ELSE iocb.started _ TRUE; }; Poll: PUBLIC PROC [o: OperationPtr] RETURNS [status: Status, retriedCount: CARDINAL] = { OPEN iocb: LOOPHOLE[o, IOCBPtr]; retriedCount _ 0; IF ~iocb.abortStatus.inProgress THEN RETURN[GetStatusForChannel[iocb.abortStatus], retriedCount]; IF nextIOCBAddress^ # nilIOP OR ~iocb.started THEN RETURN[GetStatusForChannel[LOOPHOLE[Basics.BITOR[LOOPHOLE[inProgress], Basics.BITAND[LOOPHOLE[state.status], LOOPHOLE[infoMask]]]]], retriedCount]; status _ GetStatusForChannel[GetStatus[@iocb]]; IF status = inProgress THEN { iocbContext _ [protect: iocb.writeProtection, format: IF iocb.iopBlk.sectorFormat.troyOrIBM = IBM THEN IBM ELSE Troy, density: IF iocb.iopBlk.sectorFormat.density = double THEN double ELSE single, sectorLength: iocb.iopBlk.sectorLength]; RunIOCB[@iocb] } ELSE { IF o.tries # noRetries AND status # goodCompletion THEN -- Retry operation { status _ RetryOperation[@iocb]; RETURN[status, iocb.totalRetries]}; FOR newIOCB: IOCBPtr _ iocb.nextIOCB, newIOCB.nextIOCB WHILE newIOCB # NIL DO newIOCB.started _ TRUE; IF CheckIOCB[newIOCB].inProgress THEN { iocbContext _ [protect: newIOCB.writeProtection, format: IF newIOCB.iopBlk.sectorFormat.troyOrIBM = IBM THEN IBM ELSE Troy, density: IF newIOCB.iopBlk.sectorFormat.density = double THEN double ELSE single, sectorLength: newIOCB.iopBlk.sectorLength]; RunIOCB[newIOCB]; EXIT}; ENDLOOP; } }; Reset: PUBLIC PROC [DeviceHandle] = { nextIOCBAddress^ _ nilIOP; tail _ NIL }; MaxSectorsPerTrack: PROC [context: Context] RETURNS[maxSectorsPerTrack: CARDINAL, byteIndex: EncodedSectorLength] = { 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 _ SELECT context.sectorLength FROM 64 => b128, 128 => b256, 256 => b512, 512 => b1024, ENDCASE => bUnknown; maxSectorsPerTrack _ sectorsPerTrack[byteIndex][IF context.density = single THEN single ELSE double]; }; SetContext: PUBLIC PROC [device: DeviceHandle, context: Context] RETURNS [BOOLEAN] = BEGIN byteIndex: EncodedSectorLength; [state.sectorsPerTrack, byteIndex] _ MaxSectorsPerTrack[context]; IF (byteIndex=bUnknown) OR (nextIOCBAddress^ # nilIOP) OR (context.format#IBM) THEN RETURN[FALSE]; state.user _ context; state.sectorsPerTrack _ MaxSectorsPerTrack[context].maxSectorsPerTrack; state.encodedSectorLength _ byteIndex; RETURN[TRUE]; END; SuggestedTries: PUBLIC PROC [device: DeviceHandle] RETURNS[tries: Tries] = { -- use standard retry algorithm -- RETURN[standardRetries] }; GetLogStatus: PUBLIC PROC [operationPtr: OperationPtr] RETURNS [logStatus: LogStatus] = { logStatus _ DESCRIPTOR[@state.status, 1]; RETURN[logStatus] }; MaxTracksPerFormatOperation: PUBLIC PROC [device: DeviceHandle] RETURNS[maxTracks: CARDINAL] = {maxTracks _ numOfCylinders; IF state.status.twoSided THEN maxTracks _ maxTracks * 2}; 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]; iocb.writeProtection _ iocbContext.protect; SELECT o.function FROM nop => NULL; -- allSetUp readSector, writeSector, writeDeletedSector, readID => BEGIN OPEN a: o.address; runLength _ MIN[o.count, state.sectorsPerTrack-(a.sector-1)]; Touch[addr: o.dataPtr, count: (IF o.function=readID THEN idLength ELSE iocbContext.sectorLength) * (IF o.incrementDataPointer THEN runLength ELSE 1), dirty: (o.function=readSector OR o.function=readID)]; b.samePage _ ~o.incrementDataPointer; b.bufferAddr _ o.dataPtr; b.sectorLength _ iocbContext.sectorLength; b.sectorFormat _ [ troyOrIBM: IF iocbContext.format = IBM THEN IBM ELSE Troy, density: IF iocbContext.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; gap3: ARRAY EncodedSectorLength [b128..b1024] OF ARRAY Density OF CARDINAL = [ -- Single density Double Density -- 64w-- [27, 26], -- 128w-- [42, 54], -- 256w-- [58, 84], -- 512w-- [75, 116]]; b.sectorLength _ iocbContext.sectorLength; b.formatInfo _ [ sectorLengthDiv4: iocbContext.sectorLength/2, -- (x * 2 bytes/wd)/4 encodedSectorLength: state.encodedSectorLength, sectorsPerTrack: state.sectorsPerTrack, gap3FillCharsCount: gap3[state.encodedSectorLength][IF iocbContext.density = double THEN double ELSE single]]; runLength _ MIN[ o.count, IF state.status.twoSided THEN 1 ELSE (numOfCylinders - a.cylinder)]; b.sectorFormat _ [ troyOrIBM: IF iocbContext.format = IBM THEN IBM ELSE Troy, density: IF iocbContext.density = double THEN double ELSE single]; b.diskAddress _ o.address; b.sectorCount _ runLength; iocb.thisRun _ runLength; END ENDCASE => NULL; END; CheckIOCB: PROCEDURE [iocb: IOCBPtr] RETURNS [status: DetailedStatus] = BEGIN OPEN a: iocb.operation.address, o: iocb.operation, Basics; 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 (iocbContext.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 _ LOOPHOLE[BITOR[LOOPHOLE[status], BITAND[LOOPHOLE[infoMask], LOOPHOLE[state.status]]]]; END; GetStatus: PROCEDURE [iocb: IOCBPtr] RETURNS [status: DetailedStatus] = 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); SELECT command FROM formatTrackC => BEGIN o.count _ o.count - countDone; 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; readSectorC, readIDC, writeSectorC, writeDeletedSectorC => BEGIN o.count _ o.count - countDone; IF o.incrementDataPointer THEN o.dataPtr _ o.dataPtr + (IF command=readIDC THEN idLength ELSE iocbContext.sectorLength) * countDone; [quotient: countDone, remainder: a.sector] _ Basics.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 => NULL; 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; status.notReady _ pResult.notReady; status.writeProtect _ iocbContext.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 _ Basics.BITAND[LOOPHOLE[status], LOOPHOLE[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; -- GetStatus Immediate: PROCEDURE [command: Command, subCommand: SubCommand, nopFunction: BOOL] RETURNS [initStatus: Status] = BEGIN initializationIOCB.iopBlk.command _ command; initializationIOCB.iopBlk.subCommand _ subCommand; IF nopFunction THEN initializationIOCB.operation.function _ nop; initializationIOCB.operation.device _ internalDeviceHandle; initializationIOCB.operation.tries _ 1; initStatus _ Initiate[@initializationIOCB.operation]; WHILE initStatus = inProgress DO initStatus _ Poll[@initializationIOCB.operation].status ENDLOOP END; IsTransferOp: PROC [f: Function] RETURNS [b: BOOLEAN] = BEGIN b _ SELECT f FROM readSector, readID, writeSector, writeDeletedSector, formatTrack => TRUE, ENDCASE => FALSE; END; RunIOCB: PROC [iocb: IOCBPtr] = BEGIN foo: LONG POINTER TO IOPBlock _ @iocb.iopBlk; BuildIOCB[iocb]; iocb.started _ TRUE; IF nextIOCBAddress^ = nilIOP THEN nextIOCBAddress^ _ LOOPHOLE[Basics.LowHalf[LOOPHOLE[foo]]]; END; SetupIOCBContext: PROC [iocb: IOCBPtr] = BEGIN b: LONG POINTER TO IOPBlock = @iocb.iopBlk; iocb.writeProtection _ iocbContext.protect; b.sectorLength _ iocbContext.sectorLength; b.sectorFormat _ [troyOrIBM: IF iocbContext.format = IBM THEN IBM ELSE Troy, density: IF iocbContext.density = double THEN double ELSE single]; END; Touch: PROC [addr: LONG POINTER, count: CARDINAL, dirty: BOOLEAN] = BEGIN IF dirty THEN { next: LONG POINTER = addr + 1; IF count # 0 THEN { addr^ _ 0; PrincOpsUtils.LongCopy[from:addr, to: next, nwords: count-1] }; } ELSE THROUGH [0..count) DO dummy: CARDINAL _ addr^; addr _ addr + 1; ENDLOOP; END; GetStatusForChannel: PROC [inStatus: DetailedStatus] RETURNS[outStatus: Status] = { SELECT TRUE FROM inStatus.inProgress => outStatus _ inProgress; -- operation is not yet complete inStatus.diskChange => outStatus _ diskChange; -- drive has gone not ready since last successful operation (use DiskChangeClear to reset, then resubmit operation if desired) inStatus.notReady => outStatus _ notReady; -- drive is not ready inStatus.deletedData => outStatus _ deletedData; -- sector contained deleted data mark inStatus.recordNotFound => outStatus _ recordNotFound; -- can't find record for specified disk address inStatus.crcError => outStatus _ headerError; -- bad checksum in header inStatus.dataLost => outStatus _ dataLost; -- sector contained more data than expected (from context) inStatus.writeProtect => outStatus _ writeFault; -- disk is write-protected (hardware or from context) inStatus.error => outStatus _ otherError; -- unexpected software or hardware problem ENDCASE => outStatus _ goodCompletion; -- operation has completed normally }; RetryOperation: PROC [iocb: IOCBPtr] RETURNS [status: Status] ={ multipleOfTen: CARDINAL _ 0; oneThird: CARDINAL = maxRetries / 3; recover: BOOLEAN _ FALSE; IF iocb.operation.tries = standardRetries THEN { iocb.totalRetries _ iocb.totalRetries + 1; SELECT TRUE FROM state.status.recordNotFound => { -- Always recover/recalibrate after record not found IF iocb.recNotFoundTries = 0 THEN recover _ TRUE; status _ Recalibrate[recover]; -- recalibrate/recover iocb.recNotFoundTries _ iocb.recNotFoundTries + 1; IF status # goodCompletion -- recal failed OR iocb.recNotFoundTries > maxRecNotFoundRetries THEN RETURN[GetStatusForChannel[iocb.abortStatus]]; RETURN[ReInitiate[iocb]]; -- re Initiate IOCB }; state.status.dataLost, state.status.crcError, state.status.index => { -- Recalibrate every tenth fail and do an iocb.triesDone _ iocb.triesDone + 1; multipleOfTen _ iocb.triesDone MOD oneThird; IF multipleOfTen = 0 OR iocb.triesDone > maxRetries THEN { IF iocb.triesDone = oneThird THEN recover _ TRUE; status _ Recalibrate[recover]; IF status # goodCompletion OR iocb.triesDone > maxRetries THEN RETURN[GetStatusForChannel[iocb.abortStatus]]; }; RETURN[ReInitiate[iocb]]; -- re Initate IOCB }; ENDCASE => -- other errors are fatal don't reInitiate return RETURN[GetStatusForChannel[state.status]]; } -- End Standard Retry Routine ELSE RETURN[GetStatusForChannel[state.status]]; }; Recalibrate: PROC [recover: BOOLEAN] RETURNS[status: Status] = { command: Command _ recalibrateC; IF recover THEN command _ nopC; FOR retry: CARDINAL IN [0..retryLimit) DO status _ Immediate[command, nopSC, recover]; IF status = goodCompletion THEN EXIT; ENDLOOP; RETURN[status]; }; ReInitiate: PROC [iocb: IOCBPtr] RETURNS [status: Status] = { OPEN o: LOOPHOLE[iocb, OperationPtr]; iocbContext _ [protect: iocb.writeProtection, format: IF iocb.iopBlk.sectorFormat.troyOrIBM = IBM THEN IBM ELSE Troy, density: IF iocb.iopBlk.sectorFormat.density = double THEN double ELSE single, sectorLength: iocb.iopBlk.sectorLength]; IF o.device = diskDeviceHandle THEN iocb.iopBlk _ nullIOPBlk; iocb.nextIOCB _ NIL; iocb.started _ FALSE; iocb.abortStatus _ inProgress; IF (status _ GetStatusForChannel[CheckIOCB[iocb]]) = inProgress THEN RunIOCB[iocb] ELSE iocb.started _ TRUE; }; recordNotFound: DetailedStatus = [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: DetailedStatus = [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: DetailedStatus = [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: DetailedStatus = LOOPHOLE[0]; -- ALL[FALSE] inProgress: DetailedStatus = [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: DetailedStatus = [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: DetailedStatus = [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: DetailedStatus = [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..... ÂFloppyHeadDLion.mesa Copyright Ó 1982, 1985, 1987 by Xerox Corporation. All rights reserved. Tim Diebert: May 7, 1987 9:18:53 am PDT << Comments: This is a revision of SA800HeadDLion.mesa to conform to changes made in floppyDiskFace.mesa to make the driver device independent. Cosmetic changes have been made such as renaming of variables, new definitions of certain variables ei. some status parameters will become device attribute parameters. Some procedure input and output parameters will change. Bigest change is in the polling routine, retries will be made instead of having them done at the drive level. >> ******************************************************************** C S B The Dandelion has at most one Shugart SA80x or SA85x floppy disk. The CSB resides in a resident page of memory (the IOPage). ******************************************************************** ******************************************************************** I O C B (and IOP command blocks) ******************************************************************** order of these guys is significant to Head code as well as IOP This type is used to describe the detailed status of the operation requested in contrast to Status found in the face which reports back a general status to the channel. ******************************************************************** C O N S T A N T S (canned stati can be found at the end of the module (so they won't lexically pollute code readers) ******************************************************************** Constants related to retry logic ******************************************************************** G L O B A L V A R I A B L E S ******************************************************************** **************************** PUBLIC PROCEDURES **************************** Do not submit an IOCB if there is nothing to do. This also eliminates the possiblilty of interrupting an existing IOCB. Since the occurence of a new diskChange error causes the IOCB queue to be cleared, the IOCB about to be submitted will arrive at the Head of the queue so it may be Polled immediately. if there is a diskette inserted (notReady=FALSE) then recalibrate so the FDC and IOP are synchronized. When the heads are recalibrated they are loaded => door lock engaged. If there were no disk, the FDC would ever see index pulses and never ever unload the heads, leaving the door locked which is mildly embarrassing. This may also be the first recalibrate if there was no disk loaded when the system was initialized. Assumes an "Initialize" operation has been performed so "state.status" holds valid state bits ASSUMES EXACTLY ONE FLOPPY DRIVE IS CONNECTED TO THE DANDELION This code will only handle a single controller/single device configuration. This procedure initiates an operation. The client passes an IOCB with most of the parameters filled in. We fill in the parameters that are unique to this implementation of the Head and microcode. Then we chain the IOCB onto the CSB an wake up the microcode. return (error) status if somebody polls this iocb. IOCB still in process or was never started -- This Routine determines the max number of tracks according to the current context setting. This routine requires that the device be idle (i.e., the IOCB chain to be NIL) as well as all other context to be correct before anything is changed in the head's context. Suggested Tries for standard recovery algorithm Get detailed log status Gives more complete status info to be logged in case of error Largest track count that can be supplied for formatting in Words ************************ PRIVATE PROCEDURES ************************ [512w][single] and [64w][double] are picked out of the air A procedure to check an IOCB before it is submitted to the IOP. It attempts to screen out IOCBs which shouldn't even be attempted A procedure to produce the status of an IOCB after it has been completed by the IOP. In addition to deciding if the stage just performed had any errors, this procedure decides if there are any more stages to be performed. NOTE: there is no reason to detect abortStatus.inProgress=FALSE since this routine is only called from Poll which will detect this condition before trying to call GetStatus. update disk addr and client buffer parameters if twoSided, countDone can must <= 1 since we break runs at tracks, countDone <= 1 status.na3 _ FALSE; (_ TRUE) SA800 since Mokelumne pilot thinks this is a hardware error bit status.na1 _ FALSE; status.index _ FALSE; ++ IOP never sends an index pulse back set error flag if there was an error or the run proceeds off the end of the disk. change this to use checksum when implemented in microcode GetStatusForChannel converts the detailed status into the status expected back by the channel which is defineds in the face. Not Implemented => outStatus _ cylinderError; -- can't locate specified cylinder Not Implemented => outStatus _ dataError; -- bad checksum in data Not Implemented => outStatus _ memoryError; -- dataPtr does not point to valid memory (not resident, too small write-protected, etc.) Not Implemented => outStatus _ invalidOperation; -- operation does not make sense Not Implemented => outStatus _ aborted; -- Reset has been called state.status holds results from recalibration so use iocb.abortStatus as return value extra recalibrate at end of a failing operation. state.status holds results from recalibration so use iocb.abortStatus as return value Use state.status since don't didn't do a recalibrate IF recalibrate fails after 10 trys return return (error) status if somebody polls this iocb. ******************************************************************** Constants representing Status bit settings ******************************************************************** Êјcodešœ™KšœI™IK™'—K˜K™åK˜šÏk ˜ Kšœœœœ˜.Kšœœ˜*Kšœ œ˜!KšœœX˜nK˜Kšœ œ˜!Kšœœ ˜—K˜šÐlnœ˜Kšœ7˜>Kšœ˜Kšœ˜Kšœœ˜—˜KšÏbD™DKšŸ™KšŸ|™|KšŸD™DK™Kš œœœœ Ïc˜H—˜Kš œœœœœœ=˜`—˜šœœœœ˜0Kšœ"œœ˜-——K˜˜Kš œœœœœE˜|—˜šœ!œœœ˜6Kšœ*œ˜=——˜KšŸD™DKšŸ$™$KšŸD™DK™Kš œ œœœœœ˜%Kš œ œœœœ ˜?—˜Kšœœ˜"—˜š œœœ œœ˜'Kšœ ˜/Kšœ ˜-Kšœœ $˜?Kšœ  ˜9Kšœ& ­˜ÓKšœ ˜7Kšœœ #˜™>Kšœ œœ œ€˜¡—˜Kšœ œœ œ*˜N—˜Kšœœœ œ1˜^—˜š œ œœ œœ˜-Kšœœ œœ˜AKšœœ œ"˜G——˜š œ œœ œœ˜,Kšœœ R˜oKšœœœ˜!Kšœœ '˜BKš œœ œœœ˜7Kšœœ $˜=Kšœœœ˜!Kšœœ &˜IKšœœ ˜9Kšœœ ˜1Kšœœ (˜GKšœœ ˜=Kšœœ $˜GKšœœ ˜4Kšœœ  ˜——˜˜Kšœ œ˜K˜ K˜Kšœœ˜0K˜/K˜K˜Kšœ œ#˜2K˜K˜——˜˜Kš œ œ œ œ œ˜@Kšœœ œ œ˜:Kšœœœœ˜?Kšœ œ œœ˜.—K˜Kšœœœ˜4Kšœ!œ˜-Kšœ%œ "˜U—˜Kšœœ *˜NKšœ œ ˜6Kšœœ /˜N—˜Kš œœœœœ˜6Kš œœœœœ˜3Kšœœ˜3—˜KšŸ ™ Kšœœ˜$Kšœ œ˜Kšœ œ˜K˜˜K˜—KšŸD™DKšŸ™KšŸD™DK˜K˜—˜šœœ˜šœ ˜&Kšœ œ œ&˜D—Kšœ% !˜FKšœ# %˜HKšœœ !˜AKšœ1 ˜J——˜K˜K˜Kšœ!œ œ&˜YK˜KšŸ™KšŸ™KšŸ™K˜Kšœ±™±šÏnœœ œ˜8Kšœœ˜Kšœœœ˜(Kšœœ˜ Kšœ0œ˜BKšœ¤™¤Kšœœ%œ˜8Kšœ˜——˜Kšœ]™]š ¡œœœœ˜Lšœ˜Kš œœœœœ  ˜_Kšœœœœ˜7K˜"Kšœœ4˜PK˜#Kš œœœœœœ˜5K˜$K˜ K˜K˜—Kšœ˜——˜Kšœ>™>š¡ œœœœ˜JKš œœœœœ˜S——˜Kš ¡ œœœœœ˜P—˜KšœK™Kš ¡ œœœ œœœ˜OK˜K˜Kšœœ˜ K˜'K˜K˜;Kšœ#œ˜)Kšœœ˜Kšœ˜——˜š¡œœœ˜'Kšœ;œ˜?š˜K˜:šœ˜˜Kšœœœ˜,K˜ K˜!—˜ K˜ K˜!—Kš˜—Kš˜—Kšœ˜——K˜˜Kšœ™š¡œœœœ˜DKšœœ ˜ šœœ!œ˜HKšœœœœœœ œ˜†—Kšœœ˜=Kšœœ˜Kšœœ˜K˜K˜K˜K˜K˜šœ>˜@šœ˜šœ˜Kšœ  ˜#Kšœ3 ˜H—Kšœ $˜4K˜—šœœ˜Kšœ2™2——K˜——˜š ¡œœœœ œ˜VKšœœœ ˜"K˜Kšœœœ6˜bKšœ-™-Kšœœ œœœœœœœ˜ÇK˜/šœ˜šœ˜˜.Kš œœ&œœœœ˜GKšœ œ+œœ˜NK˜(—K˜K˜—šœ˜šœœœ ˜JK˜!Kšœ˜#—šœ4œ œ˜MKšœœ˜šœœ˜'šœ0˜0Kš œœ)œœœ˜JKšœ œ-œœ˜QK˜+—K˜Kšœ˜—Kšœ˜—K˜—K˜———˜Kš¡œœœ6œ˜N—˜KšœZ™Zš¡œœœœ#˜tš œœ"œœ œ˜VKšœ %˜*Kš  œ˜&Kš  œ˜&Kš  œ˜&Kš  œ˜'—Kšœ œœ5œ ˜uKšœ0œœœ ˜eK˜——˜Kšœ«™«š ¡ œœœ*œœ˜ZK˜K˜AKšœœœœœœœ˜cK˜K˜GK˜&Kšœœ˜ Kšœ˜——˜Kšœ/™/š¡œœœœ˜JKšœ "œœ˜?—K˜KšœU™Uš¡ œœœœ˜Xšœ œœ˜@K˜——Kšœ@™@š ¡œœœœ œ˜`Kšœœœ˜V—K˜KšŸD™D—˜š¡ œ œœœ˜DKšœœœœ˜+Kšœ œ˜K˜Kšœ!œœ˜/K˜Kšœœœ˜9K˜&K˜+šœ ˜Kšœœ  ˜šœ6œœ˜OKšœ œ.˜=Kšœ!œœ œœœ œ#œ˜ÍK˜%K˜K˜*Kšœœœœœœœœœ ˜Kšœ ˜5Kšœ $˜@Kšœ *˜EKšœ˜—šœœœ˜'š œœ#œœ œ˜JKšœ:™:Kšœ %˜*Kš  œ˜&Kš  œ˜&Kš  œ˜&Kš  œ˜'—K˜*Kš œ? œœœœ ˜›šœ œ˜Kšœ œœœ ˜M—˜Kš œ œœœœœ˜:Kšœ œœœ ˜D—K˜K˜K˜Kš˜—Kšœœ˜—Kšœ˜——˜Kšœ‚™‚š¡ œ œœ˜GKšœœ6˜@š¡ œœœœ˜1Kšœœœœ˜H—Kšœœœ˜>K˜Kšœœ˜.šœœ˜Kšœ#œ˜(K˜/Kšœœœ6˜hKšœœœ œœœœ œœœ+˜ÍKšœœ˜Kš œœœœ œœ œ˜r—Kšœ˜——˜Kšœ™š¡ œ œœ˜GKšœœ#˜-K˜Kšœ  ˜>Kšœ œœœ˜1Kšœ-™-šœœ˜Kšœ œ"˜5šœ ˜šœ˜K˜š œ œœœ˜$Kšœ$™$Kšœœ˜1Kšœ6˜=—Kšœ˜—šœ:˜@K˜šœ˜˜Kšœœœ œ'˜O——˜*KšœA˜A—K˜Kšœ-™-š œ œœœ˜$Kšœœ˜1Kšœ6˜=—Kšœ˜—Kšœœ˜—Kšœ˜——˜Kšœ ˜)Kš œ+œœœœœ˜§K˜#šœ™KšœH™H—K˜#Kšœ*œ˜BKšœ™Kšœ=™=šœ ˜šœ˜K˜)K˜#K˜#Kšœ0œ'œ˜jKšœ˜—šœ˜#K˜#K˜#Kšœ0œ'œ˜jKšœ˜—šœ˜K˜#Kšœ/œ'œ˜iKšœ˜—šœ˜K˜3K˜!Kšœ˜—K˜9Kšœ˜—KšœQ™QKšœœœ œ˜JKšœ"œœ ˜OKšœ œœ˜9K˜)Kšœ  ˜——˜š ¡ œ œ9œœ˜wK˜,K˜2Kšœ œ-˜@K˜;K˜'K˜5šœ˜ K˜8Kš˜—Kšœ˜——˜š ¡ œœœœ˜=šœœ˜KšœDœ˜IKšœœ˜—Kšœ˜——˜š¡œœ˜%Kšœœœœ˜-K˜Kšœœ˜šœœ˜"Kšœœœ˜;—Kšœ˜——˜š¡œœ˜.Kšœœœœ˜+K˜+K˜*Kšœœœœœœœœœ ˜Kšœ˜——˜Kšœ9™9š ¡œœœœ œ œ˜Išœœ˜Kšœœœ ˜šœ œ˜K˜ K˜?K˜—šœ˜šœ œœ ˜/K˜—Kšœ˜——Kšœ˜——˜Kšœ|™|š¡œœœ˜Sšœœœ˜Kšœ0  ˜PKšœ1 ~˜¯Kšœ- ˜BKšœQ™QKšœ1 %˜VKšœ9 /˜hKšœ/ ˜HKšœB™BKšœ- :˜gKšœ1 5˜fKšœ†™†KšœR™RKšœA™AKšœ+ *˜UKšœ  #˜J—K˜K˜—š¡œœœ˜BKšœœ˜Kšœ œ˜$Kšœ œœ˜šœ(œ˜0K˜+šœœ˜˜!šœ 4˜6Kšœœ œ˜2Kšœ ˜5K˜2Kšœ ˜+šœ/œ˜7Kšœ4™4Kšœ ™ Kšœ)˜/—Kšœ ˜-—K˜—˜Dšœ )˜+Kšœ1™1K˜$Kšœœ ˜,šœœ˜9šœœœ œ˜3K˜šœœ˜>Kšœ4™4Kšœ ™ Kšœ(˜.—K˜——Kšœ ˜,—K˜—šœ 1˜?Kšœ%˜+—šœ ˜Kšœ4™4———Kšœœ$˜/K˜K˜—š¡ œœ œœ˜>˜"Kšœ œ˜Kšœ)™)šœœœœ˜+K˜,Kšœœœ˜%—Kšœ˜šœ ˜K˜———š¡ œœœ˜;šœœœ˜'˜.Kš œœ&œœœœ˜GKšœ œ+œœ˜NK˜(—Kšœœ˜=Kšœœ˜Kšœœ˜K˜šœ=˜?Kšœ˜Kšœ2™2Kšœœ˜—K˜—K˜—K˜KšŸD™DKšŸ*™*KšŸD™Dš!œ/œœ œœ œœœ œ œœœœ œ œ œœ˜µK˜—š!œ(œœ œœ œœœ œ œœœœ œ œ œœ˜®K˜—š!œ*œœ œœ œœœ œ œœœœ œ œ œœ˜°K˜—Kšœ œ  ˜;K˜š!œ*œœ œœ œœœ œ œœœœ œ œ œœ˜¯K˜—š!œ,œœ œœ œœœ œ œœœœ œ œ œœ˜³K˜—š!œ+œœ œœ œœœ œ œœœœ œ œ œœ˜¬K˜—Kš!œ(œœ œœ œœœ œ œœœœ œ œ œœ˜­—˜Kšœ˜——…—gúš