// TfsInit.bcpl // Copyright Xerox Corporation 1979, 1980, 1981 // Last modified April 30, 1981 8:28 PM by Taft get "Altofilesys.d" get "Disks.d" get "Tfs.d" get "Streams.d" get "AltoDefs.d" external [ // TFS procedures defined here TFSInit TFSSetDisk TFSTryDisk TFSDiskModel // TFS procedures defined elsewhere TFSReadBTpage TFSActOnPages TFSVirtualDA TFSRealDA TFSWritePages TFSCreateFile TFSDeletePages TFSAssignDiskPage TFSReleaseDiskPage TFSInitializeCbStorage TFSDoDiskCommand TFSGetCb TFSClose TFSNonEx DoRecovery TFSWaitQuiet TFSModShift TFSModShiftA TFSSilentBoot TFSCreateDDMgr OpenDD // OS ActOnDiskPages SetWorkingDir OpenFile Closes Gets ReadBlock WriteBlock Allocate Free Zero MoveBlock SysErr StartIO DefaultArgs Idle Noop // Statics TFSLock TFSLeaveDisplay OsVersion AltoVersion ] manifest RTC=#430 //Real time clock //---------------------------------------------------------------------------- let TFSInit(zone, initmode, driveNumber, ddMgr, freshDisk; numargs na) = valof //---------------------------------------------------------------------------- [ DefaultArgs(lv na, -1, 0, 0, 0, 0) manifest minOsVersion = 16 if OsVersion ls minOsVersion then SysErr(minOsVersion, ecOsVersion) // Alto II convention is to set location 613 to -1. // The reason we recompute AltoVersion is to ensure it is correct even // in a boot file that was created on a different type of machine. AltoVersion = (table [ #61014; #1401 ])() // VERS @#613 = AltoVersion<<VERS.eng gr 1 if AltoVersion<<VERS.eng ge 4 then [ // Dolphin or Dorado TFSLeaveDisplay = true // don't need to turn display off TFSSilentBoot = Noop // don't silent boot when finishing TFSModShift = TFSModShiftA // use software ECC correction subroutine ] // separate out the file system number (if T-300) let fsNumber = driveNumber rshift 8 driveNumber = driveNumber & #377 // First, try to select a disk that does not exist. If // selection succeeds, it is because the controller does not // have a multi-drive multiplexor attached. Note that the // important bit in the disk number is 10b, because this will // cause none of the 8 copies of the selection logic to be // enabled. (The drive-select decoder is only 4 bits wide!!) // 37 is used rather than 17 because 37 is specifically defined // as a nonexistent drive on the Dorado. let p = TFSTryDisk(37b) //See if controller hooked up if p eq 0 then resultis 0 //No -- no dice if driveNumber ne 0 & p eq 1 then resultis 0 //Not multi-drive controller p = TFSTryDisk(driveNumber) //Now look for exact drive if p ne 1 then resultis 0 //Not there or not on-line // Setup structures needed for subsequent accesses let disk = Allocate(zone, lTFSDSK) Zero(disk, lTFSDSK) TFSSetDisk(disk, initmode, driveNumber) disk>>TFSDSK.zone = zone // do a restore if the drive appears to be in trouble if KBLK>>KBLK.DeviceCk % KBLK>>KBLK.SeekInc then DoRecovery(disk, diskRestore) // set up physical disk shape let model = DetermineDiskModel(disk) disk>>TFSDSK.model = model // Set up file system related parameters. // Largest possible file system has 2↑16-1 pages. If the physical disk // is bigger than this, then multiple file systems must be constructed. // Each file system (except the last) consists of the largest integral // number of tracks (nVTracks) such that the file system contains fewer // than 2↑16 pages, and is offset from the beginning of the disk by an // integral number of tracks (firstVTrack). // The correct general computation for this is: // nVTracks = (2↑16-1)/(nHeads*nSectors) // firstVTrack = fsNumber*nVTracks // However, this is messy to do in BCPL because it requires unsigned // division. Since the Trident T-300 is the only disk large enough // to require this treatment, it is therefore handled as a special case. manifest maxT300nVTracks = 383 // (2↑16-1)/(19*9) if (model ne 300 & fsNumber ne 0) % fsNumber gr 2 then [ Free(zone, disk); resultis 0 ] //no such fs on pack disk>>TFSDSK.firstVTrack = fsNumber*maxT300nVTracks disk>>TFSDSK.nVTracks = disk>>TFSDSK.nTracks-disk>>TFSDSK.firstVTrack if model eq 300 & disk>>TFSDSK.nVTracks gr maxT300nVTracks then disk>>TFSDSK.nVTracks = maxT300nVTracks // TFSInit (cont'd) // Setup directory fp for this disk let fidTFSSysdir = table [ #100000 ; 100 ] let fpTFSSysDir = lv (disk>>TFSDSK.fpTFSSysDirblk) MoveBlock(lv fpTFSSysDir>>FP.serialNumber, fidTFSSysdir, lSN) fpTFSSysDir>>FP.version = 1 fpTFSSysDir>>FP.leaderVirtualDa = 1 SetWorkingDir("<SysDir.",fpTFSSysDir,disk) // Open directory, and setup important fp's for this disk, etc. unless freshDisk do [ let DAs = vec 2 DAs!1 = 1 if ActOnDiskPages(disk, 0, DAs+1, fpTFSSysDir, 0, 0, DCreadnD, 0, 0, 0, 0, 0, true) ls 0 then [ Free(zone, disk); resultis 0 ] //no SysDir let dds = OpenFile("DiskDescriptor", ksTypeReadOnly, 0, 0, disk>>TFSDSK.fpDiskDescriptor, 0, zone, 0, disk) if dds eq 0 then [ Free(zone, disk); resultis 0 ] //no DiskDescriptor ReadBlock(dds, lv disk>>TFSDSK.kd, lTFSKDHeader) if disk>>TFSDSK.version ne TFSKDversion then [ Closes(dds); Free(zone, disk); resultis 0 ] //version incompatible // Count free pages in the file system for i=1 to TFSwordsPerPage-lTFSKDHeader do Gets(dds) let pages=0 for i=1 to disk>>TFSDSK.diskBTsize do [ let w=Gets(dds) test w eq 0 ifso pages = pages+16 ifnot if w ne -1 then for j=0 to 15 do if ((w rshift j)&1) eq 0 then pages = pages+1 ] // Now fill in free count where it is supposed to be: disk>>TFSDSK.freePages = pages Closes(dds) ] // set up DD manager, if needed if initmode ne 0 then [ if ddMgr eq 0 then ddMgr = TFSCreateDDMgr(zone) disk>>TFSDSK.ddMgr = ddMgr OpenDD(ddMgr, disk) ] // return handle on disk object resultis disk ] //---------------------------------------------------------------------------- and TFSTryDisk(drive) = valof //---------------------------------------------------------------------------- // Try to access disk and return: // 0 if controller appears not to work // 1 if disk appears to be there // 2 if disk appears to be absent [ while TFSLock ne 0 do Idle() TFSLock = -1 //lock out all other access TFSWaitQuiet(false) let oldDrive = KBLK>>KBLK.drive if oldDrive uge #17 then oldDrive = 0 let result = TryDisk(drive) // If selected a nonexistent drive, must leave controller pointing // at the previously-selected drive, which is assumed to exist. // (Note that drive 0 must exist or the controller won't work -- // this is a hardware restriction.) if result eq 2 then TryDisk(oldDrive) TFSLock = 0 resultis result ] //---------------------------------------------------------------------------- and TryDisk(drive) = valof //---------------------------------------------------------------------------- [ StartIO(#20) //turn off and reset controller [ //repeat KBLK>>KBLK.ptr = 0 KBLK>>KBLK.drive = drive%100000b //Force drive select KBLK>>KBLK.Status = -1 KBLK>>KBLK.track = -1 //Force seek next time let timedOut=nil // See if unit responds. Let each StartIO work for 1/20 second. // If Status has not been set after last StartIO, timedOut will // be true. The reason for several StartIO's is that we may // be trying to select a non-existent disk, which will not // provide sector pulses and corresponding wakeups. for reTries = 1 to 4 do //May have to give several StartIO's [ // for a non-ex disk StartIO(#40) // Causes microcode to start timedOut = false let inTime = @RTC [ if KBLK>>KBLK.Status ne -1 then break if (@RTC-inTime) gr 1 then timedOut=true Idle() ] repeatuntil timedOut unless timedOut break ] if timedOut then resultis 0 //Controller not hooked up ] repeatuntil KBLK>>KBLK.WrLate eq 0 // Which status bit to test? A drive will select if power is on // (even if the disk is not spinning): NotSelected is therefore only // an indication of a non-ex or fully powered down drive. The OnLine // indication is best, although if the drive is in the middle of // a restore, it is conceivable that OnLine goes away. resultis KBLK>>KBLK.NotOnLine eq 0? 1, 2 ] //---------------------------------------------------------------------------- and TFSDiskModel(disk) = disk>>TFSDSK.model //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- and DetermineDiskModel(disk) = valof //---------------------------------------------------------------------------- // Determines what model disk is attached, puts default disk shape // parameters in the disk object, and returns the disk model number: // 80 Trident T-80 // 300 Trident T-300 // 4004 Shugart SA-4004 // 4008 Shugart SA-4008 [ // The following parallel tables define the disk parameters for the // disk models currently known about. Determination of disk model // is done by selecting heads and seeing what happens -- each model // disk has a different maximum head number. // nHeadsT!(i-1) but not nHeadsT!i is a legal head for disk model modelT!i, // 0 <= i <= maxModel. nHeadsT must be monotonic! manifest maxModel = 3 let modelT = table [ 4004; 80; 4008; 300 ] let nTracksT = table [ 202; 815; 202; 815 ] let nHeadsT = table [ 4; 5; 8; 19 ] let nSectorsT = table [ 8; 9; 8; 9 ] while TFSLock ne 0 do Idle() TFSLock = -1 TFSWaitQuiet(false) let modelIndex = 0 [ // repeat let kcb = vec lKCB Zero(kcb, lKCB) kcb>>KCB.drive = disk>>TFSDSK.driveNumber // Issue no real commands -- just select a head kcb>>KCB.head = nHeadsT!modelIndex kcb>>KCB.ID = dcbID KBLK>>KBLK.ptr = kcb while KBLK>>KBLK.ptr ne 0 do Idle() // Trident disks raise CylOvfl and Shugart disks raise SecOvfl // when an illegal head is selected. if KBLK>>KBLK.CylOvfl % KBLK>>KBLK.SecOvfl then break unless KBLK>>KBLK.WrLate do modelIndex = modelIndex + 1 ] repeatuntil modelIndex eq maxModel TFSLock = 0 DoRecovery(disk, diskReset) //turn off error indications // Now set physical disk shape parameters disk>>TFSDSK.nDisks = 1 disk>>TFSDSK.nTracks = nTracksT!modelIndex disk>>TFSDSK.nHeads = nHeadsT!modelIndex disk>>TFSDSK.nSectors = nSectorsT!modelIndex resultis modelT!modelIndex ] //---------------------------------------------------------------------------- and TFSSetDisk(disk, initmode, drivenumber) be //---------------------------------------------------------------------------- // TFSSetDisk sets up constant part of disk structure. // disk points to the TFSDSK structure for drive drivenumber [ // Access dispatches disk>>DSK.ActOnDiskPages = TFSActOnPages disk>>DSK.WriteDiskPages = TFSWritePages disk>>DSK.CreateDiskFile = TFSCreateFile disk>>DSK.DeleteDiskPages = TFSDeletePages disk>>DSK.AssignDiskPage = TFSAssignDiskPage disk>>DSK.ReleaseDiskPage = TFSReleaseDiskPage disk>>DSK.CloseDisk = TFSClose disk>>DSK.VirtualDiskDA = TFSVirtualDA disk>>DSK.RealDiskDA = TFSRealDA disk>>DSK.InitializeDiskCBZ = TFSInitializeCbStorage disk>>DSK.DoDiskCommand = TFSDoDiskCommand disk>>DSK.GetDiskCb = TFSGetCb // Deny access in certain cases // WriteDiskPages is NOT included because it has several uses that do not // require modifications to the bit table. if initmode eq 0 then [ disk>>DSK.CreateDiskFile = TFSNonEx disk>>DSK.DeleteDiskPages = TFSNonEx disk>>DSK.AssignDiskPage = TFSNonEx disk>>DSK.ReleaseDiskPage = TFSNonEx ] // FP's disk>>DSK.fpSysDir = lv disk>>TFSDSK.fpTFSSysDirblk disk>>DSK.fpDiskDescriptor = lv disk>>TFSDSK.fpTFSDDblk disk>>DSK.fpWorkingDir = lv disk>>TFSDSK.fpTFSWDblk // Working dir disk>>DSK.nameWorkingDir=lv disk>>TFSDSK.WDNameblk // Other parameters disk>>DSK.lnPageSize = TFSlnWordsPerPage disk>>DSK.driveNumber = drivenumber disk>>DSK.retryCount = 16 disk>>DSK.totalErrors = 0 disk>>DSK.diskKd = lv disk>>TFSDSK.kd disk>>DSK.lengthCBZ = lTFSCBZ disk>>DSK.lengthCB = lCB disk>>TFSDSK.initmode = initmode ]