// diexbfs.Bcpl // New Alto basic file system //last modified April 20, 1978 12:31 PM by R Bates //Thsi is not BFS at all!!! I really changed a lot of stuff // N O T E ! ! ! The whole "disk" object depends on errors of any kind // stopping subsequent transfers. For example, WriteBlock will in general // write a page from the stream buffer, write a few pages from core, // and READ the last page into the stream buffer. If the disk is allowed // to continue down its chain, even though an error occurred on the first // (write of stream buffer), the stream buffer will become clobbered with // bad data from the last page. Retrying will write bad data into the first // page!!! //get "AltoFileSys.d" //Definitions of structures in the file system //get "Disks.d" //Definitions for the "disk class" get "Diex.defs" //Definitions for driving the Model 31/44 disk // Outgoing procedures external [ // Outgoing procedures GetCb; InitializeCbStorage; DoDiskCommand // incoming procedures Usc; Idle; ReturnTo; DisableInterrupts; EnableInterrupts; DefaultArgs; SysErr ] manifest [ ecUnRecovDiskError = 1101 ecBadAction = 1103 ecBfsBug = 1104 //BFSrestoreAndSeek = #525 ] static [ nSectors = 12; nHeads = 2; nTracks = 203 ; nDisks = 1; DiskSel=0 ] static [ @TP0=0; @TP1=0; @TP2=0; LastCb=0; Aexit ] // *** DELETE FOR TFS.bcpl //--------------------------------------------------------------------------- let VirtualDA(lvrealDA) = valof //--------------------------------------------------------------------------- [ let realDA = @lvrealDA if realDA eq eofDA then resultis eofDA resultis (((((realDA<<DA.disk * nTracks) + realDA<<DA.track) * nHeads) + realDA<<DA.head) * nSectors) + realDA<<DA.sector ] //--------------------------------------------------------------------------- and RealDA(virtualDA, lvrealDA) = valof //--------------------------------------------------------------------------- [ // Div(x) returns virtualDA/x and leaves the remainder in virtualDA let Div = table [ #55001 // sta 3 savedPC,2 #25004 // lda 1 firstArg,2 #155000 // mov 2 3 #111000 // mov 0 2 #102460 // mkzero 0 0 #61021 // div #77400 // Swat #171000 // mov 3 2 #45004 // sta 1 firstArg,2 #35001 // lda 3 savedPC,2 #1401 // jmp 1,3 ] let realDA = 0 if virtualDA ne eofDA then [ realDA<<DA.sector = Div(nSectors) realDA<<DA.head = Div(nHeads) realDA<<DA.track = Div(nTracks) realDA<<DA.disk = virtualDA ] @lvrealDA = realDA // Return true if disk address appears legal. resultis (virtualDA ge 0 & virtualDA ls nDisks) ] //--------------------------------------------------------------------------- let InitializeCbStorage(zone, firstPage, length, retry; numargs na) be //--------------------------------------------------------------------------- [ if na ge 4 then [ Zero(zone, length) zone>>CBZ.length = length zone>>CBZ.retry = retry ] zone>>CBZ.currentPage = firstPage let e = lv zone>>CBZ.queueVec rv e = 0; zone>>CBZ.queueTail = e e = e +1; zone>>CBZ.queueHead = e let cb = zone+zone>>CBZ.length [ cb = cb-lCB; if Usc(cb,e) le 0 break cb>>CB.zone = zone cb>>CB.status = DSTfreeStatus rv e = cb; e = e +1 ] repeat zone>>CBZ.endQueueVec = e ] //--------------------------------------------------------------------------- and NextCb(zone, pointer) = valof //--------------------------------------------------------------------------- [ let t = zone!pointer let u = t +1 if Usc(u,zone>>CBZ.endQueueVec) ge 0 then u = lv zone>>CBZ.queueVec zone!pointer = u resultis t ] //--------------------------------------------------------------------------- and DoDiskCommand(cb, CA, DA, fp, pageNumber, action) be //--------------------------------------------------------------------------- // Expects command and label to both be zeroed on entry, or // otherwise appropriately initialized [ let z = cb>>CB.zone cb>>CB.headerAddress = lv cb>>CB.header let la = cb>>CB.labelAddress if la eq 0 then [ la = lv cb>>CB.label cb>>CB.labelAddress = la ] if cb>>CB.dataAddress eq 0 then cb>>CB.dataAddress = CA if cb>>CB.normalWakeups eq 0 then cb>>CB.normalWakeups = z>>CBZ.normalWakeups if cb>>CB.errorWakeups eq 0 then cb>>CB.errorWakeups = z>>CBZ.errorWakeups if la>>DL.fileId.SNword1 eq 0 then //label data is zero so set Fid to 'unused' [ la>>DL.fileId.SNword1 = -1 la>>DL.fileId.SNword2 = -1 la>>DL.fileId.version = -1 la>>DL.pageNumber = pageNumber ] cb>>CB.truePageNumber = pageNumber // Compute real disk address //if DA ne fillInDA then RealDA(DA, lv cb>>CB.diskAddress) **gets a real DA cb>>CB.diskAddress = DA// **gets a real DA cb>>CB.diskAddress.disk = DiskSel cb>>CB.header.diskAddress = DA// **gets a real DA cb>>CB.header.diskAddress.disk = PackType if action eq BFSrestoreAndSeek then [ cb>>CB.diskAddress.restore = 1 action = msb rshift offset DC.seekOnly ] if cb>>CB.diskAddress.restore then @lastDiskAddress = -1 //let actNumber = action-diskMagic //if actNumber ls 0 % actNumber gr 6 % (action&diskMagicMask) ne diskMagic then //SysErr(action,ecBadAction) //Someday, may let non-standard action become the command! cb>>CB.command = action % DCaS //cb>>CB.command = (table //[ //diskRead * (DCheaderx + DClabelx + DCdatax) + DCaS; //diskCheck * DCheaderx + diskRead * (DClabelx + DCdatax) + DCaS; //diskCheck * (DCheaderx + DClabelx) + diskRead * DCdatax + DCaS; //diskWrite * (DCheaderx + DClabelx +DCdatax) + DCaS; //diskCheck * DCheaderx + diskWrite * (DClabelx + DCdatax) + DCaS; //diskCheck * (DCheaderx + DClabelx) + diskWrite * DCdatax + DCaS; //msb rshift offset DC.seekOnly + DCaS //])!actNumber //if disk>>DSK.driveNumber then cb>>CB.command.exchangeDisks = 1 // QueueDiskCommand(cb) DisableInterrupts() SendCmmd: let p = nil let np = rv nextDiskCommand if np ne 0 then [ [ p = np; np = p>>CB.nextCommand ] repeatuntil np eq 0 p>>CB.nextCommand = cb ] // If there are no disk commands queued at the moment, be very careful // about plopping down a pointer to our cb. We must be sure that the // previous cb for this zone has NOT encountered an error (it's OK // if it is free or has been transferred already, but not OK if there // is an error or if it has not been transferred at all) if rv nextDiskCommand eq 0 then [ let t = z!CBZqueueTail if t eq (lv z>>CBZ.queueVec) then t = z>>CBZ.endQueueVec let prevCb = t!-1 let stat = prevCb>>CB.status & DSTgoodStatusMask if prevCb eq 0 then Bug() if stat ne 0 & (stat & DSTerrorBits) eq 0 then @nextDiskCommand = cb ] EnableInterrupts() p = NextCb(z, CBZqueueTail) if @p ne 0 then Bug(); @p = cb ] //--------------------------------------------------------------------------- and GetCb(zone, dontClear; numargs na) = valof //--------------------------------------------------------------------------- [ let t = NextCb(zone, CBZqueueHead) let cb = rv t; if cb eq 0 then Bug(); rv t = 0 LastCb = cb let i = 0 [ if (cb>>CB.status & DSTdoneBits) ne 0 break i=i+1 if i eq 0 then break //dont wait for ever **** ] repeat if TP1 then [ cb>>CB.status = TP1; until @nextDiskCommand eq 0 loop ] // remove seal cb>>CB.command = cb>>CB.command & #377 //cb>>CB.status = cb>>CB.status & #177377//mask of bit #400 so it can be used for check err let transfer = false //Flag true if cb was used in a transfer // Various ways the cb can be found usable: ErrCode: [ // This is the test for errors let s = cb>>CB.status & DSTgoodStatusMask if s eq DSTfreeStatus break unless cb>>CB.diskAddress.restore do transfer = true if s eq DSTgoodStatus then break // We should probably discriminate among the various kinds of error // Wait for disk to stop spinning until @nextDiskCommand eq 0 loop let ec = zone>>CBZ.errorCount +1 zone>>CBZ.errorCount = ec saveErr(zone>>CBZ.DAs+ cb>>CB.truePageNumber, s xor #400, ec)//**** DELETE for BFS.bcpl if CheckInput() then ReturnTo(Aexit) // **** DELETE FOR TFS.bcpl if ec ge maxEC then [ // Unrecoverable error //DO WHAT I WANT HERE t=zone>>CBZ.cleanupRoutine if t then if t(cb,false) then CheckData(cb,false) ec = 0 cb>>CB.truePageNumber = cb>>CB.truePageNumber+1 zone>>CBZ.errorCount=0 ] let DA = cb>>CB.diskAddress zone>>CBZ.errorDA = DA if cb>>CB.status.finalStatus eq dsCheckError then zone>>CBZ.checkError = true // Now rebuild the zone: InitializeCbStorage(zone, cb>>CB.truePageNumber) // If the error count is large enough, initiate a restore. Note // that the command is issued, but we do not wait to be sure // it completes. if ec gr ErrRes then DoDiskCommand(GetCb(zone), 0, DA, 0, 0, BFSrestoreAndSeek) ReturnTo(zone>>CBZ.retry) ] repeat if transfer then [ t = zone>>CBZ.cleanupRoutine if t then if t(cb, true) do [ cb>>CB.status.CompairErr = 0; goto ErrCode ] zone>>CBZ.currentNumChars = cb>>CB.labelAddress>>DL.numChars zone>>CBZ.errorCount = 0 ] unless na ge 3 & dontClear do Zero(cb, lVarCB) resultis cb ] //--------------------------------------------------------------------------- and Bug() be SysErr(nil,ecBfsBug) //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- and ActOnPages(CAs, DAs, fp, firstPage, lastPage, action, lvNumChars, lastAction, fixedCA, cleanupRoutine; numargs na) = valof //--------------------------------------------------------------------------- [ //let GetNextDA(cb) be //[ //let t = lv ((cb>>CB.zone>>CBZ.DAs)!(cb>>CB.truePageNumber)) //if t!1 eq fillInDA then //t!1 = VirtualDA(lv cb>>CB.labelAddress>>DL.next) //if t!(-1) eq fillInDA then //t!(-1)=VirtualDA(lv cb>>CB.labelAddress>>DL.previous) //] let dummy = nil let reslt = nil DefaultArgs(lv na, -6, lv dummy, action, 0 ,0) let zone = vec CBzoneLength InitializeCbStorage(zone, firstPage, CBzoneLength, Aretry) zone>>CBZ.DAs = DAs zone>>CBZ.cleanupRoutine = cleanupRoutine Aretry: [ // Note that each cb is used twice: to hold the DL for // page i-1, and then to hold the KCB for page i. It isn't // reused until the command for page i is done, and that is // guaranteed to be after the DL for page i-1 is no longer // needed, since everything is done strictly sequentially by // page number. // Note: Inside this loop, there may be disk commands pending // (i.e. pointed to by nextDiskCommand). DoDiskCommand // initiates them (also GetCb may initiate a restore). Hence // do not simply "return" from inside the loop unless you are // absolutely sure that no disk activity is queued. Aexit = AexitLabel let cb = GetCb(zone) reslt = lastPage for i = zone>>CBZ.currentPage to lastPage do [ let a = action; if i eq lastPage then a = lastAction if a<<DC.headerAction eq diskSkip then loop if DAs!0 eq eofDA then [ reslt = i-1; break ] let nextCb = GetCb(zone) //cb>>CB.labelAddress=((DAs!(i+1) eq fillInDA)? //lv nextCb>>CB.diskAddress,lv nextCb>>CB.label) let Wbuff = WhereIsWbuff(i) cb>>CB.labelAddress = lv cb>>CB.label test PattCnt eq 2 //check to see what type of pattern is being tested ifso MoveBlock(cb>>CB.labelAddress,Wbuff+8,8) //Random data ifnot MoveBlock(cb>>CB.labelAddress,Wbuff,8) //Other Pattern if action<<DC.dataAction eq diskCheck then cb>>CB.dataAddress = Wbuff DoDiskCommand(cb, (CAs eq 0 ? fixedCA, CAs!i),DAs!i, fp, i, a) cb = nextCb ] while @zone>>CBZ.queueHead ne 0 do GetCb(zone) ] @lvNumChars = zone>>CBZ.currentNumChars resultis reslt AexitLabel: //*** DELETE FOR TFS resultis LastCb>>CB.truePageNumber ]