<> <> <> <> <> <> <> <> <> <<1. If more than one drive, or if different kinds of Shugarts are used, Get attributes (at least) has to be fixed.>> <<2 Some of the error flags returned will be different than those returned by the D0. For example, the Dandelion controller cannot simultaneously read and verify header fields. As a consequence, whenever an error is encountered during a header operation, the "wrongSector" status is returned. It is also not possible to issue any sort of timeout flag since the Head has no clock.>> <<3. Runs of Header noops/reads are supported, they are done a page-at-a-time on the SA1000; more work probably has to be done here and for rr and rrr.>> <<4. The calculation of the low part of the IOCB's read address depends upon knowledge that IOCB's live in the first 64K, as well as the page number being in the rightmost part of the flag word.>> <<5. Rename (within mesa) some of the fields within the IOCB. For example, the incrementDataPtr bit is the high order bit of the DataPageNumber, and should be a seperate boolean rather than 100000B. Similarly, the head bits are now or'ed into freezeCmd, but should be seperatly defined.>> <<6. Consider calculating the low half of the physical IOCB address once in initiate (and stashing it in the IOCB) rather then each time through the Next IOCB loop.>> <<7. Does deviceCleanup do the right thing.>> <<8. what should incrementDataPtr on ?,?,nop do??>> <<9. get rid of spin counters someday.>> <<10. The increment field of the IOCB is the bit 0 of the page number. this conflicts with the fat memory boards and with large virtual address spaces and should be moved.>> DIRECTORY Basics USING[ BITAND, BITNOT, BITOR, LongNumber, LongMult], DDC, DeviceCleanup USING [Await, Item, Reason], DiskFace USING [Label], DLionInputOutput USING [GetRealPage, IOPage, Input, Output], PrincOps USING [wordsPerPage], PrincOpsUtils USING [LowHalf, PageNumberForAddress], SA4000Face, SA4000HeadDLionConstants; SA4000HeadDLion: PROGRAM IMPORTS Basics, DeviceCleanup, DLionInputOutput, PrincOpsUtils EXPORTS SA4000Face SHARES SA4000Face = { OPEN DDC, SA4000Face, SA4000HeadDLionConstants; <> <> <> Base: TYPE = LONG BASE POINTER; CSB: TYPE = MACHINE DEPENDENT RECORD [ cylinder(0): CARDINAL, next(1): Base RELATIVE POINTER TO ChannelCommand, tail(2): IOCBshortPtr, -- last iocb. Used by initiate transferMask(3): WORD, -- naked notify mask. needRecalibrate(4:0..15): BOOL]; -- TRUE => recalibrate required. csb: LONG POINTER TO CSB = LOOPHOLE[DLionInputOutput.IOPage+0B]; <> FilePageRep: TYPE = MACHINE DEPENDENT RECORD[ n(0): SELECT OVERLAID * FROM cedar => [cedar(0): INT], pilot => [ filePageLo(0): CARDINAL, pilotHi(1): RECORD[ filePageHi(0:0..6): [0..128), -- restricts to 23-bit page numbers (32-bit byte counts) pad1 (0:7..12): [0..64) _ 0, -- always zero immutable (0:13..13): BOOL _ FALSE, -- valid only in label of page 0 temporary (0:14..14): BOOL _ FALSE, -- valid only in label of page 0 zeroSize (0:15..15): BOOL _ FALSE ] ], raw => [ lowHalf(0): CARDINAL, highHalf(1): CARDINAL ] ENDCASE ]; <> p : Base = LOOPHOLE[LONG[0]]; step: WORD = 200B; -- as part of Output causes heads to move directionInBit: WORD = 100B; -- on => heads move inwards (higher # cyls) driveSelect: WORD = 2000B; -- must be on for drive to listen clearSA1000WriteFault: WORD = 0B; -- reset disk to clear SA1000 fault sa1000PagesPerTrack: CARDINAL = 16; sa1000TracksPerCylinder: CARDINAL = 4; --Assumes SA1004 sa1000Cylinders: CARDINAL = 256; clearSA4000WriteFault: WORD = 3000B; -- reset disk to clear SA4000 fault sa4000PagesPerTrack: CARDINAL = 28; sa4000TracksPerCylinder: CARDINAL = 8; --Assumes SA4008 sa4000Cylinders: CARDINAL = 202; q2040PagesPerTrack: CARDINAL = 16; q2040TracksPerCylinder: CARDINAL = 8; -- assumes q2040 q2040Cylinders: CARDINAL = 512; diskDeviceHandle: DeviceHandle = LOOPHOLE[1]; --#nulldeviceHandle errorMask: WORD = 77B; -- selects error bits in the device status KCtl: CARDINAL = 3; -- number of disk control register KStatus: CARDINAL = 3; -- number of disk status register startDiskCode: WORD = 2040B; -- control word sent to start the disk <> globalStateSize: PUBLIC CARDINAL _ 0; -- no fixup IOCB needed. nullDeviceHandle: PUBLIC DeviceHandle _ LOOPHOLE[177777B]; operationSize: PUBLIC CARDINAL _ SIZE[IOCB]; driveIsSA1000: BOOL; pagesPerTrack: CARDINAL; sectorsPerCyl: CARDINAL; maxCylinderNum: CARDINAL; -- last cylinder maxHeadNum: CARDINAL; -- last head resetDisk: WORD; -- = clearSA?000WriteFault <> totalErrors: PUBLIC CARDINAL _ 0; -- total errors reported by Poll pollRaceSpin: LONG CARDINAL _ 0; recalSeekSpin: LONG CARDINAL _ 0; potentialKludgeErrors: LONG CARDINAL _ 0; <> <> <> <> <> GetDeviceAttributes: PUBLIC PROC [device: DeviceHandle] RETURNS [cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL] = { RETURN[maxCylinderNum + 1, maxHeadNum + 1, 0, pagesPerTrack]}; GetTrueDeviceAttributes: PUBLIC PROC [device: DeviceHandle] RETURNS [cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL] = { [cylinders, movingHeads, fixedHeads, sectorsPerTrack] _ GetDeviceAttributes[device]; }; <> GetNextDevice: PUBLIC PROC [device: DeviceHandle] RETURNS [DeviceHandle] = { RETURN[IF device = nullDeviceHandle THEN diskDeviceHandle ELSE nullDeviceHandle]}; Initialize: PUBLIC PROC [t: WORD, globalState: GlobalStatePtr] = { csb.next _ nil; csb.tail _ nil; csb.transferMask _ t; csb.needRecalibrate _ TRUE; <> Reset[diskDeviceHandle]; -- reset the disk }; InitializeCleanup: PUBLIC PROC = { OPEN DeviceCleanup; item: Item; sCsb: CSB; DO reason: Reason = Await[@item]; SELECT reason FROM turnOff, disconnect, kill => {WHILE GetDDCHardwareBits[].firmwareBusy DO ENDLOOP; sCsb _ csb^}; turnOn => <> <> {sCsb.cylinder_csb.cylinder; sCsb.needRecalibrate_TRUE; csb^ _ sCsb}; ENDCASE ENDLOOP }; <> <> <> <> Initiate: PUBLIC PROC [o: OperationPtr] = { OPEN iocb: LOOPHOLE[o, IOCBlongPtr]; iocbShort: IOCBshortPtr = LOOPHOLE[PrincOpsUtils.LowHalf[@iocb]]; sectorsInPresentCyl: CARDINAL; <> { <> pageAddr: LONG POINTER _ o.dataPtr; pageCount: CARDINAL = IF o.incrementDataPtr THEN o.pageCount ELSE 1; PageCrossCheck[iocbShort]; SELECT DataOperation[o.command] FROM read => THROUGH [0..pageCount) DO pageAddr^ _ 0; pageAddr _ pageAddr + PrincOps.wordsPerPage ENDLOOP; write => THROUGH [0..pageCount) DO d: CARDINAL = pageAddr^; pageAddr _ pageAddr + PrincOps.wordsPerPage; ENDLOOP; ENDCASE; }; <> IF ~csb.needRecalibrate THEN iocb.iocbState _ startSeek ELSE {iocb.iocbState _ startRecal; csb.needRecalibrate _ FALSE}; sectorsInPresentCyl _ sectorsPerCyl - (o.clientHeader.head*pagesPerTrack+o.clientHeader.sector); iocb.destCylinder _ IF o.pageCount <= sectorsInPresentCyl THEN o.clientHeader.cylinder ELSE o.clientHeader.cylinder + ((o.pageCount - sectorsInPresentCyl - 1)/sectorsPerCyl) + 1; <> <> o.deviceStatus.a _ 177777B; iocb.savedError _ inProgress; iocb.nextIOCB _ nil; iocb.label _ o.labelPtr^; iocb.unusedLabel _ 0; <> [] _ NextIOCBState[@iocb, IF csb.next = nil THEN csb.cylinder ELSE p[csb.tail].destCylinder]; <> IF csb.next = nil THEN { csb.next _ LOOPHOLE[iocbShort+entryOffset]; Output[startDiskCode, @iocb]} ELSE { <> p[csb.tail].nextIOCB _ LOOPHOLE[iocbShort + entryOffset]; IF p[csb.tail].iocbState = transferFinish THEN <> p[csb.tail].transferCommand.finishTransfer.nextIOCB _ p[csb.tail].nextIOCB; }; csb.tail _ iocbShort; }; CantGetHere: ERROR = CODE; Poll: PUBLIC PROC [o: OperationPtr] RETURNS [status: Status] = { <> OPEN iocb: LOOPHOLE[o, IOCBlongPtr]; originalDeviceStatus: WORD _ 0; IF ~LOOPHOLE[ o.deviceStatus.b _ LOOPHOLE[GetDDCHardwareBits[]], DDCHardwareBits].firmwareBusy AND o.deviceStatus.a = 0 THEN status _ hardwareError ELSE IF o.deviceStatus.a = 0 THEN RETURN[inProgress] ELSE { OPEN Basics; IF BITAND[originalDeviceStatus _ o.deviceStatus.a, errorMask]=0 THEN csb.cylinder _ iocb.presentCylinder; -- update cyl only if no error. status _ NextIOCBState[@iocb, csb.cylinder]; }; SELECT status FROM inProgress => <> <> <> IF o.deviceStatus.a#0 THEN ERROR CantGetHere ELSE Output[startDiskCode, @iocb]; goodCompletion => { <> <> <> <> IF (csb.next _ iocb.nextIOCB) # nil AND ~LOOPHOLE[originalDeviceStatus, DDCStatusBits].firmwareBusy THEN { <> WHILE GetDDCHardwareBits[].firmwareBusy DO pollRaceSpin _ pollRaceSpin+1 ENDLOOP; Output[ startDiskCode --, @p[LOOPHOLE[csb.next-entryOffset, IOCBshortPtr]]--]; }; o.labelPtr^ _ iocb.label; <> o.deviceStatus.a _ originalDeviceStatus; <> }; ENDCASE => { <> IF GetDDCHardwareBits[].writeFault THEN Output[resetDisk, NIL]; <> csb.next _ nil; <> <> <> <<@p[LOOPHOLE[csb.next - entryOffset, IOCBshortPtr]];>> <<----find next IOCB from chain.>> <<----Recalculate the next IOCB considering the cylinder may be screwed up.>> <<----BE CONSERVATIVE FOR NOW.>> <> <> <> <> <<[] _ NextIOCBState[next, csb.cylinder]; ++ think on it...>> <> <> totalErrors _ totalErrors + 1; <> <> <> <> IF status # labelCheck THEN o.labelPtr^ _ iocb.label; o.deviceStatus.a _ originalDeviceStatus; -- restore for diagnostics }; }; Recalibrate: PUBLIC PROC [device: DeviceHandle] = {csb.needRecalibrate _ TRUE}; Reset: PUBLIC PROC [device: DeviceHandle] = {Output[resetDisk, NIL]}; Start: PROC = { IF (driveIsSA1000 _ GetDDCHardwareBits[].driveIsSA1000) THEN { <> IF GetDDCKTest[].notSector THEN { <> pagesPerTrack _ sa1000PagesPerTrack; sectorsPerCyl _ sa1000TracksPerCylinder * sa1000PagesPerTrack; maxCylinderNum _ sa1000Cylinders - 1; maxHeadNum _ sa1000TracksPerCylinder - 1; resetDisk _ clearSA1000WriteFault; } ELSE { <> pagesPerTrack _ q2040PagesPerTrack; sectorsPerCyl _ q2040TracksPerCylinder * q2040PagesPerTrack; maxCylinderNum _ q2040Cylinders - 1; maxHeadNum _ q2040TracksPerCylinder - 1; resetDisk _ clearSA1000WriteFault; }; } ELSE { <> pagesPerTrack _ sa4000PagesPerTrack; sectorsPerCyl _ sa4000PagesPerTrack * sa4000TracksPerCylinder; maxCylinderNum _ sa4000Cylinders - 1; maxHeadNum _ sa4000TracksPerCylinder - 1; resetDisk _ clearSA4000WriteFault; }; }; <> GetDDCHardwareBits: PROC RETURNS [DDCHardwareBits] = INLINE { RETURN[LOOPHOLE[Basics.BITNOT[DLionInputOutput.Input[KStatus]]]]}; <> <>Faces>Private>ddc.mesa>> <> <> <<|rd dsk|rd dsk|dsk out|dsk wrt|~seek|~dir| b |~IW |TTL| ~ |~drv|~b |~ttl| ~ |~rd |~wrt|>> <<|clock | data | clock | data |cmplt| in |horiz|rduc|vid|sctr|slct|vert|vidi|step|gate|gate|>> <<| 8 4 2 1 | 8 4 2 1 | 8 4 2 1 | 8 4 2 1 |>> <<| 1 | 4 2 1 | 4 2 1 | 4 2 1 | 4 2 1 | 4 2 1 |>> <<|rd dsk|rd dsk|dsk out|dsk wrt|~seek|~dir| b |~IW |TTL| ~ |~drv|~b |~ttl| ~ |~rd |~wrt|>> <<|clock | data | clock | data |cmplt| in |horiz|rduc|vid|sctr|slct|vert|vidi|step|gate|gate|>> DDCKTest: TYPE = MACHINE DEPENDENT RECORD [ diskReadClk (0: 0.. 0): BOOL, diskReadData (0: 1.. 1): BOOL, diskOutputClk (0: 2.. 2): BOOL, diskWriteData (0: 3.. 3): BOOL, notSeekComplete (0: 4.. 4): BOOL, notDirectionIn (0: 5.. 5): BOOL, bHoriz (0: 6.. 6): BOOL, notReduceIW (0: 7.. 7): BOOL, ttlVideo (0: 8.. 8): BOOL, notSector (0: 9.. 9): BOOL, notDriveSelect (0:10..10): BOOL, notBVert (0:11..11): BOOL, notTtlvideo (0:12..12): BOOL, notStep (0:13..13): BOOL, notReadGate (0:14..14): BOOL, notWriteGate (0:15..15): BOOL]; GetDDCKTest: PROC RETURNS[DDCKTest] = INLINE{ KTest: CARDINAL = 6; RETURN[LOOPHOLE[Basics.BITNOT[DLionInputOutput.Input[KTest]]]]}; <> <> <> <> <<(deviceStatus=0).>> ImpossibleProcessingState: ERROR = CODE; NextIOCBState: PROC [iocb: IOCBlongPtr, curCyl: CARDINAL] RETURNS [iocbStatus: Status] = { OPEN o: iocb.operation; runLength: CARDINAL; -- number of pages to transfer physIOCBAddr: CARDINAL; -- low 16 bits of IOCB physical address IF o.deviceStatus.a = 177777B THEN <> <> iocbStatus _ IF (SELECT HeaderOperation[o.command] FROM verify => FALSE, IN [noop..read] => (LabelOperation[o.command] = write OR DataOperation[o.command] = write OR o.pageCount > 1), ENDCASE => --write-- (driveIsSA1000 AND (o.pageCount#pagesPerTrack OR o.clientHeader.sector#0)) ) THEN hardwareError ELSE inProgress ELSE { <> hwdStatus: DDCStatusBits = LOOPHOLE[o.deviceStatus.a]; <> hadError: BOOL = (Basics.BITAND[LOOPHOLE[hwdStatus], errorMask] # 0); <> <> IF ~hadError AND iocb.iocbState IN [transfer..labelRead] THEN { OPEN cH: o.clientHeader, l: iocb.label; <> <> IF HeaderOperation[o.command] = read THEN IF (cH.sector _ cH.sector + 1) = sectorsPerCyl THEN {cH.head _ cH.head + 1; cH.sector _ 0}; IF cH.head > maxHeadNum THEN {cH.head _ 0; cH.cylinder _ cH.cylinder + 1}; <> IF LabelOperation[o.command] # read THEN { <> kludge: LONG POINTER TO FilePageRep = LOOPHOLE[@l.filePage]; kludge^ _ [pilot[pilotHi: [filePageHi: kludge.pilotHi.filePageHi], filePageLo: kludge.filePageLo]]; }; }; <> <> IF iocb.iocbState IN [transfer..transferFinish] THEN { runLength _ iocb.countThisStage-iocb.transferParm.sectorCount; o.pageCount _ o.pageCount - runLength; IF o.incrementDataPtr THEN o.dataPtr _ o.dataPtr + Words[runLength]; }; <> <> <<1. seek out by the current cylinder number, this should get us directly to cylinder zero.>> <<2. If the Track00 signal is set, set the StartSeek and try to set the new state from there. If not on track 0, step in 16 tracks one at a time looking for track 0. This is done in case we overshot.>> <<3. If track 00 is found at any step, set StartSeek and start a data transfer, if any. If all 16 steps are exhausted, step out maxCylinderNum+20 cylinders , one at a time.>> <<4. If the Track00 is found at any step, stop and preceed as above. If not, post "SeekError" and return. If a Seek operation is needed to put the heads over the desired track, The next state _ "DoSeek", the distance and direction are computed and control is passed to the third section of NextIOCBState to build the Seek IOCB. The destination cylinder is also posted in the CSB. If a data transfer operation is needed, we first determine whether the first sector is to be found by verifying headers or by counting from the index mark. It is assumed that if the second method is to be employed (the Header Operation = read or noop), exactly one sector will be read and there will be no write operations involved. Next the maximum length of the transfer is computed. A run of pages may be broken before its natural finish for a number of reasons. First, page zero of any file is transferred independently of all other pages. This allows special treatment of its flags. Runs of pages are broken at 64K page boundries so the microcode need never increment two words of page labels. They are also broken at cylinder boundries so a seek may be inserted. The distance in pages to the closest boundry determines the length of the transfer.>> <> <> SELECT TRUE FROM -- first set the status for any of the disastrous faults iocb.savedError # inProgress => iocbStatus _ iocb.savedError; hwdStatus.memoryFault => { <> IF LabelOperation[o.command] # read THEN { kludge: LONG POINTER TO FilePageRep = LOOPHOLE[@iocb.label.filePage]; kludge.filePageLo _ kludge.filePageLo - 1; }; iocbStatus _ hardwareError; }; hwdStatus.driveNotReady => iocbStatus _ notReady; hwdStatus.overrun, hwdStatus.writeFault => iocbStatus _ hardwareError; ENDCASE => iocbStatus _ inProgress; <seek, 1=>header, 2=>label, 3=>data.>> SELECT hwdStatus.lastField FROM 0 => iocbStatus _ IF hadError THEN hardwareError ELSE inProgress; 1 => SELECT TRUE FROM hwdStatus.verifyError, hwdStatus.CRCError => iocbStatus _ wrongSector; iocbStatus = inProgress => iocbStatus _ hardwareError; <> ENDCASE; 2 => SELECT TRUE FROM hwdStatus.CRCError => iocbStatus _ labelError; -- bad label hwdStatus.verifyError => iocbStatus _ labelCheck; <> iocbStatus = inProgress => iocbStatus _ hardwareError; <> ENDCASE; 3 => SELECT TRUE FROM hwdStatus.verifyError, hwdStatus.CRCError => iocbStatus _ dataError ENDCASE; ENDCASE; <> }; <> statusCount[iocbStatus] _ statusCount[iocbStatus] + 1; IF iocbStatus=inProgress OR iocbStatus=labelCheck THEN DO stateCount[iocb.iocbState] _ stateCount[iocb.iocbState] + 1; SELECT iocb.iocbState FROM startRecal => { iocb.presentCylinder _ IF curCyl=0 THEN 1 ELSE 0; -- force movement iocb.iocbState _ recalSeek; EXIT; }; recalSeek => { <> UNTIL GetDDCHardwareBits[].seekComplete DO --wait for hardware-- recalSeekSpin _ recalSeekSpin+1 ENDLOOP; IF GetDDCHardwareBits[].track00 THEN { curCyl _ iocb.presentCylinder _ csb.cylinder _ 0; iocb.iocbState _ startSeek; LOOP; } ELSE { -- not on cylinder 0 yet, try stepping in 16 cylinders csb.cylinder _ 0; -- just to make things consistent iocb.presentCylinder _ 1; iocb.iocbState _ recalStepIn; -- start process of stepping in EXIT; }; }; recalStepIn => <> SELECT TRUE FROM GetDDCHardwareBits[].track00 => { <> curCyl _ iocb.presentCylinder _ csb.cylinder _ 0; iocb.iocbState _ startSeek; LOOP; }; iocb.presentCylinder < 16 => { <> iocb.presentCylinder _ iocb.presentCylinder + 1; iocb.iocbState _ recalStepIn; -- continue stepping in EXIT; }; ENDCASE => { <> csb.cylinder _ maxCylinderNum + 20; iocb.presentCylinder _ maxCylinderNum + 19; <> iocb.iocbState _ recalStepOut; -- start process of stepping out EXIT; }; recalStepOut => <> SELECT TRUE FROM GetDDCHardwareBits[].track00 => { <> curCyl _ iocb.presentCylinder _ csb.cylinder _ 0; iocb.iocbState _ startSeek; LOOP; }; iocb.presentCylinder > 0 => { <> iocb.presentCylinder _ iocb.presentCylinder - 1; iocb.iocbState _ recalStepOut; -- continue process of stepping out EXIT; }; ENDCASE => { < quit>> iocbStatus _ hardwareError; iocb.iocbState _ recalStepOut; -- loop here if get called again EXIT; }; startSeek => <> <> <> <> IF (iocb.presentCylinder _ curCyl) = o.clientHeader.cylinder THEN {iocb.iocbState _ startTransfer; LOOP} -- on cyl, start transfer ELSE IF o.clientHeader.cylinder > maxCylinderNum THEN {iocbStatus _ seekTimeout; -- can't get there; clientError-- EXIT} ELSE { <> iocb.presentCylinder _ o.clientHeader.cylinder; iocb.iocbState _ doSeek; EXIT; }; doSeek, -- => {iocb.iocbState _ startTransfer; LOOP}; startTransfer => SELECT TRUE FROM driveIsSA1000 AND HeaderOperation[o.command]=write => {iocb.iocbState _ eraseSA1000Track; runLength _ 1; EXIT}; (o.pageCount=0) => {iocb.iocbState _ transferFinish; LOOP}; ENDCASE => {iocb.iocbState _ transfer; LOOP}; eraseSA1000Track => { kludge: LONG POINTER TO FilePageRep = LOOPHOLE[@iocb.label.filePage]; kludge.filePageLo _ kludge.filePageLo - 1; o.clientHeader.sector _ 0; iocb.iocbState _ transfer; LOOP; }; transfer => SELECT iocbStatus FROM inProgress => { <> headerOp: FieldOperation = HeaderOperation[o.command]; sectorsLeftInCyl: CARDINAL = sectorsPerCyl - (o.clientHeader.head*pagesPerTrack + o.clientHeader.sector); runLength _ MIN[o.pageCount, sectorsLeftInCyl]; IF headerOp # write AND LabelOperation[o.command]#read THEN { OPEN l: iocb.label; <> kludge: LONG POINTER TO FilePageRep = LOOPHOLE[@iocb.label.filePage]; IF kludge.filePageLo = 0 THEN { IF kludge.pilotHi.filePageHi = 0 THEN runLength _ 1} -- pg 0 ELSE runLength _ MIN[runLength, 65535 - kludge.filePageLo + 1]; IF driveIsSA1000 AND headerOp IN [noop..read] THEN runLength _ 1; }; IF o.pageCount = runLength THEN iocb.iocbState _ transferFinish ELSE { IF runLength = sectorsLeftInCyl THEN <> IF iocb.presentCylinder >= maxCylinderNum THEN iocbStatus _ seekTimeout -- should be client error ELSE iocb.presentCylinder _ curCyl + 1; iocb.iocbState _ transfer; }; EXIT; }; labelCheck => {iocbStatus _ inProgress; iocb.iocbState _ labelRead; EXIT}; ENDCASE => ImpossibleProcessingState; transferFinish => SELECT iocbStatus FROM inProgress => {iocbStatus _ goodCompletion; iocb.iocbState _ transferFinish; EXIT}; labelCheck => {iocbStatus _ inProgress; iocb.iocbState _ labelRead; EXIT}; ENDCASE => ImpossibleProcessingState; labelRead => { <> <> <> o.clientHeader _ o.diskHeader; -- restore Header saved when IOCB set up o.command _ iocb.savedIOCBCmd; -- restore command saved when IOCB set up IF ~MatchLabels[o.labelPtr, @iocb.label] OR iocb.unusedLabel # 0 THEN { hardLabelChecks _ hardLabelChecks+1; iocbStatus _ labelCheck; EXIT} ELSE <> IF o.labelPtr.dontCare = iocb.label.dontCare THEN { iocbStatus _ labelError; EXIT } ELSE { softLabelChecks _ softLabelChecks+1; iocb.iocbState _ startTransfer; LOOP}; }; ENDCASE => ImpossibleProcessingState; ENDLOOP; { <> <> OPEN lp: LOOPHOLE[iocb, Basics.LongNumber]; wPP: CARDINAL = PrincOps.wordsPerPage; physIOCBAddr _ (LOOPHOLE[DLionInputOutput.GetRealPage[lp.lowbits/wPP], CARDINAL]*wPP) + (lp.lowbits MOD wPP); }; IF iocbStatus # inProgress THEN { OPEN sC: iocb.seekCommand, fS: iocb.seekCommand.finishSeek; <> <> <> <> <> <> sC _ seekCmdBlock[dummyIOCB]; -- set the dummy IOCB commands fS.nextIOCB _ iocb.nextIOCB; -- microcode chains to next IOCB fS.transferMask _ csb.transferMask; sC.negDistanceAddr _ sC.negDistanceAddr + physIOCBAddr; fS.statusAddr _ fS.statusAddr + physIOCBAddr; } ELSE SELECT iocb.iocbState FROM IN [recalStepIn..recalStepOut] => { OPEN sC: iocb.seekCommand, fS: iocb.seekCommand.finishSeek; <> <> <> <> sC _ seekCmdBlock[step]; -- set the Step IOCB commands IF iocb.iocbState = recalStepIn THEN { <> sC.controlWd1 _ Basics.BITOR[sC.controlWd1, directionInBit]; sC.controlWd2 _ Basics.BITOR[sC.controlWd2, directionInBit]; sC.waitCmd _ Basics.BITOR[sC.waitCmd, directionInBit]; <> }; sC.sendCtlAddr _ sC.sendCtlAddr + physIOCBAddr; fS.nextIOCB _ nil; -- stop after this IOCB completes fS.transferMask _ csb.transferMask; sC.negDistanceAddr _ sC.negDistanceAddr + physIOCBAddr; fS.statusAddr _ fS.statusAddr + physIOCBAddr; }; recalSeek, doSeek => { OPEN sC: iocb.seekCommand, fS: iocb.seekCommand.finishSeek; <> <> <> sC _ seekCmdBlock[seek]; -- set the Seek IOCB commands IF iocb.presentCylinder > curCyl THEN { <> sC.controlWd1 _ Basics.BITOR[sC.controlWd1, directionInBit]; sC.controlWd2 _ Basics.BITOR[sC.controlWd2, directionInBit]; <> sC.waitCmd _ Basics.BITOR[sC.waitCmd, directionInBit]; <> sC.negDistance _ curCyl - iocb.presentCylinder; } ELSE <> sC.negDistance _ iocb.presentCylinder - curCyl; sC.sendCtlAddr _ sC.sendCtlAddr + physIOCBAddr; fS.nextIOCB _ nil; -- stop after this IOCB completes fS.transferMask _ csb.transferMask; sC.negDistanceAddr _ sC.negDistanceAddr + physIOCBAddr; fS.statusAddr _ fS.statusAddr + physIOCBAddr; }; eraseSA1000Track, transfer, transferFinish => { OPEN tP: iocb.transferParm; headerOp: FieldOperation = HeaderOperation[o.command]; <> <> <> <> <> <> tP.headerOp _ headerOpBlock[headerOp]; tP.labelOp _ labelOpBlock[LabelOperation[o.command]]; tP.dataOp _ dataOpBlock[DataOperation[o.command]]; tP.dataOp.dataPgNum _ IF DataOperation[o.command] = noop THEN PrincOpsUtils.PageNumberForAddress[DLionInputOutput.IOPage] ELSE (IF o.incrementDataPtr THEN 100000B ELSE 0) + PrincOpsUtils.PageNumberForAddress[o.dataPtr]; { OPEN m: tP.miscCmds, cH: o.clientHeader; m _ miscCmdBlock[driveIsSA1000]; -- set findSectMk and Stop commands m.freezeCmd _ m.freezeCmd + cH.head*8*256; -- "or" head into bits 1..4 m.findSectMkCmd _ m.findSectMkCmd + cH.head*8*256; }; iocb.countThisStage _ tP.sectorCount _ runLength; <> <> <> <> <> <> IF headerOp IN [noop..write] THEN { <> <> OPEN sC: iocb.seekCommand, cH: o.clientHeader; IF driveIsSA1000 THEN { <> sC _ seekCmdBlock[findSA1000Sector0]; sC.controlWd2 _ sC.controlWd2 + physIOCBAddr; <> SELECT headerOp FROM IN [noop..read] => { OPEN Basics; tP.sectorCount _ cH.sector + 1; <> <> <> tP.dataOp.dataPgNum _ BITAND[tP.dataOp.dataPgNum, 77777B]; <> }; ENDCASE => --write, can't be verify here { <> IF iocb.iocbState = eraseSA1000Track THEN tP.dataOp.dataCnt _ (sa1000PagesPerTrack+4) * PrincOps.wordsPerPage }; } ELSE { <> sC _ seekCmdBlock[findSA4000Sector0]; sC.negDistance _ SA4000SectorDistance[cH.sector]; sC.negDistanceAddr _ sC.negDistanceAddr + physIOCBAddr; sC.sendCtlAddr _ sC.sendCtlAddr + physIOCBAddr; sC.initRegsParm _ sC.initRegsParm + physIOCBAddr; }; tP.failCount _ 1; -- quit immediately on Header Field errors } ELSE -- headerOp=verify { OPEN sC: iocb.seekCommand; <> <> <> sC _ seekCmdBlock[plainTransfer]; sC.controlWd1 _ sC.controlWd1 + physIOCBAddr; tP.failCount _ IF driveIsSA1000 THEN 3*2*sa1000PagesPerTrack --(3 field/sector) ELSE 2*sa4000PagesPerTrack --(1 field/sector) <> }; { <> OPEN tC: iocb.transferCommand; IF iocb.presentCylinder = curCyl + 1 THEN tC _ transferCmdBlock[doStep] <> ELSE { <> tC _ transferCmdBlock[noStep]; IF iocb.iocbState = transferFinish THEN <> tC.finishTransfer.nextIOCB _ iocb.nextIOCB; tC.stepCmdWd1 _ tC.stepCmdWd1 + physIOCBAddr; }; tC.parmBlockAddr _ tC.parmBlockAddr + physIOCBAddr; tC.finishAddr _ tC.finishAddr + physIOCBAddr; tC.finishTransfer.statusAddr _ tC.finishTransfer.statusAddr + physIOCBAddr; tC.finishTransfer.transferMask _ csb.transferMask; }; tP.headerOp.headerAddr _ tP.headerOp.headerAddr + physIOCBAddr; tP.labelOp.labelAddr _ tP.labelOp.labelAddr + physIOCBAddr; }; -- of setting up a normal transfer IOCB labelRead => { OPEN tP: iocb.transferParm; <> o.labelPtr^ _ iocb.label; -- save expected label iocb.savedIOCBCmd _ o.command; -- save failed operation o.command _ vr; -- read the offending label o.diskHeader _ o.clientHeader; -- save correct header <> iocb.presentCylinder _ curCyl; -- we will be on this cylinder <> <> <> <> <> tP.sectorCount _ 1; -- read one label only tP.failCount _ IF driveIsSA1000 THEN 2*3*sa1000PagesPerTrack -- (3 fields/sector) ELSE 2*sa4000PagesPerTrack; -- (1 field /sector) <> tP.headerOp _ headerOpBlock[verify]; -- verify header tP.labelOp _ labelOpBlock[read]; -- then read the label tP.dataOp _ dataOpBlock[noop]; -- forget the data tP.dataOp.dataPgNum _ PrincOpsUtils.PageNumberForAddress[DLionInputOutput.IOPage]; { OPEN m: tP.miscCmds, cH: o.clientHeader; m _ miscCmdBlock[driveIsSA1000]; -- set findSectMk, Stop m.freezeCmd _ m.freezeCmd + cH.head*8*256; -- "or" head into bits 1..4 m.findSectMkCmd _ m.findSectMkCmd + cH.head*8*256; }; iocb.seekCommand _ seekCmdBlock[plainTransfer]; -- just looking iocb.seekCommand.controlWd1 _ iocb.seekCommand.controlWd1 + physIOCBAddr; { OPEN tC: iocb.transferCommand; tC _ transferCmdBlock[noStep]; -- no poststep tC.finishTransfer.nextIOCB _ nil; -- stop after label read tC.stepCmdWd1 _ tC.stepCmdWd1 + physIOCBAddr; tC.parmBlockAddr _ tC.parmBlockAddr + physIOCBAddr; tC.finishAddr _ tC.finishAddr + physIOCBAddr; tC.finishTransfer.statusAddr _ tC.finishTransfer.statusAddr + physIOCBAddr; tC.finishTransfer.transferMask _ csb.transferMask; }; tP.headerOp.headerAddr _ tP.headerOp.headerAddr + physIOCBAddr; tP.labelOp.labelAddr _ tP.labelOp.labelAddr + physIOCBAddr; }; ENDCASE; o.deviceStatus.a _ 0; -- mark iocb as not having been seen by uCode iocb.savedError _ iocbStatus; -- this will be used in no-op IOCB's RETURN[iocbStatus]; }; MatchLabels: PROC[p1, p2: LONG POINTER TO DiskFace.Label] RETURNS[BOOL] = INLINE { <> MatchableLabel: TYPE = MACHINE DEPENDENT RECORD [ a, b, c, d, dontCare: RECORD [UNSPECIFIED, UNSPECIFIED] ]; { OPEN m1: LOOPHOLE[p1^, MatchableLabel], m2: LOOPHOLE[p2^, MatchableLabel]; RETURN[m1.a=m2.a AND m1.b=m2.b AND m1.c=m2.c AND m1.d=m2.d] }; }; Output: PROC [cmd: UNSPECIFIED, i: IOCBlongPtr _ NIL] = INLINE { DLionInputOutput.Output[cmd, KCtl]}; IOCBCrossesPage: ERROR = CODE; PageCrossCheck: PROC [i: IOCBshortPtr] = INLINE { IF LOOPHOLE[i, CARDINAL] MOD PrincOps.wordsPerPage >= PrincOps.wordsPerPage - SIZE[IOCB] THEN ERROR IOCBCrossesPage; }; Words: PROC [sectors: CARDINAL] RETURNS [LONG CARDINAL] = INLINE { RETURN[Basics.LongMult[sectors, PrincOps.wordsPerPage]]}; <> stateCount: ARRAY DDC.ProcessingState OF INT _ ALL[0]; statusCount: ARRAY SA4000Face.Status OF INT _ ALL[0]; hardLabelChecks: INT _ 0; softLabelChecks: INT _ 0; Start[]; }.... LOG Time: June 13, 1980 3:40 PM, By: DDavies Action: start change to Dandelion Head Time: June 16, 1980 5:11 PM, By: Davies Action: end first pass at Dandelion Head Time: June 17, 1980 5:12 PM, By: Davies Action: prepare for compilation Time: June 23, 1980 3:51 PM, By: Forrest Action: Fix bug, add IOCB check. Reformat/maulOver. Time: July 16, 1980 1:25 PM, By: Forrest Action: Take out code supporting initial debugging, add ability to format parts of SA4000 tracks. Time: September 9, 1980 2:33 PM, By: Forrest Action: Reformat (again), work on Label check bug. Time: September 9, 1980 2:33 PM, By: Sandman Action: AddressFault bug. Time: January 22, 1981 3:10 PM, By: Gobbel Action: Don't reset cylinder when first building IOCB. Reformat. Time: February 11, 1981 1:53 PM, By: Forrest Action: Add code (state) to format SA1000 track. Time: February 14, 1981 2:25 PM, By: Gobbel Action: Fix bug in NextIOCBState. Time: March 31, 1981 9:44 AM, By: Forrest Action: change the pre adjustment or pageCOunt and DataPointer in NextIOCBState to adjustment after the IOCB is cooked. Remark: July 22, 1983 9:23 am: Sturgis: add test for quantum disk, assume only diff from SA1000 is shape of disk. (required adding q2040 constants, and modifying Start.) Added GetDDCKTest. Time: January 20, 1984 4:42 pm, By: Andrew Birrell Action: Convert to Cedar 5.1. Time: January 20, 1984 4:42 pm, By: Bob Hagmann Action: add GetTrueDeviceAttributes <> <> <>