// ScavInit -- once only initialization
// Copyright Xerox Corporation 1979, 1980, 1981, 1982
// Last modified April 30, 1982  8:58 PM by Boggs

get "AltoDefs.d"
get "AltoFileSys.d"
get "Streams.d"
get "Disks.d"
get "BFS.d"

external
[
// outgoing procedures
InitScavenger

// incoming procedures
EditDisk; TryDisk; DiskToCursor; Ws; Confirm
StartLog; SplitPuts; SysErr; FindDShape
ScavAssignDiskPage; ScavReleaseDiskPage

CreateSimpleDspStream; CreateKeyboardStream
SimpleDspEraseBits; SimpleDspCharWidth; SimpleDspDCB
InitializeZone; Allocate; Endofs; Gets; Puts
BFSInit; BFSCreateFile; BFSDeletePages
MoveBlock; Zero; MyFrame; Idle
PutTemplate

// outgoing statics
sysZone; sysDisk; dsp
maxVDA; alterFlag; label; data

// incoming statics
gacha10; keys; log
]

static
[
sysZone; sysDisk; dsp
maxVDA; alterFlag; label; data
]

manifest
[
dspHeight = 100	//scan lines
dspWidth = 36	//words
]

//-----------------------------------------------------------------------------------------
let InitScavenger() be
//-----------------------------------------------------------------------------------------
[
// create cursor
MoveBlock(cursorBitMap, table [ 177700b; 77600b; 37400b; 17700b; 7360b;
 3074b; 1017b; 3; 0; 0; 0; 167112b; 105252b; 44252b; 25352b; 167244b ], 16)
Idle = DiskToCursor

// create sysZone
manifest stackLimit = 335b
let freeBegin = @stackLimit
let freeEnd = MyFrame() -1500
@stackLimit = freeEnd
if freeEnd-freeBegin ls 0 then freeBegin = freeEnd-77777b
sysZone = InitializeZone(freeBegin, freeEnd-freeBegin)

// create keyboard, display, and log streams
log = StartLog()
keys = CreateKeyboardStream()
dsp = CreateSimpleDspStream(Allocate(sysZone, dspHeight*dspWidth, false, true),
 dspWidth, dspHeight, gacha10)
dsp>>ST.puts = SplitPuts
let dspDCB = SimpleDspDCB(dsp)
dspDCB>>DCB.indentation = 3
let spaceDCB = Allocate(sysZone, lDCB, false, true); Zero(spaceDCB, lDCB)
spaceDCB>>DCB.height = 20
spaceDCB>>DCB.next = dspDCB
@displayListHead = spaceDCB
Ws("Alto File System Scavenger of September 29, 1982")

// default label and data buffers -- used by everyone
label = Allocate(sysZone, lDL)
data = Allocate(sysZone, 256)

// give user a chance to change disks
let eng = (table [ 61014b; 1401b ])()<<VERS.eng
if eng le 3 then if Confirm("*NDo you want to change disks?") then
   [
   Ws("*NChange them now.  Type any character to proceed.")
   Gets(keys)
   ]
WaitForDisk(0)

sysDisk = BFSInit(sysZone, false, 0, 0, true)
sysDisk>>DSK.AssignDiskPage = ScavAssignDiskPage
sysDisk>>DSK.ReleaseDiskPage = ScavReleaseDiskPage
sysDisk>>DSK.CreateDiskFile = BFSCreateFile
sysDisk>>DSK.DeleteDiskPages = BFSDeletePages

// InitScavenger (cont'd)
let res = valof  //dummy control structure to break two loops
[
let status = TryDisk(0, 0, 0, 1) & DSTgoodStatusMask
let ds = data + FindDShape(data) +1
test status eq DSTgoodStatus & ds>>DSHAPE.nHeads eq 2 &
 (ds>>DSHAPE.nDisks eq 1 % ds>>DSHAPE.nDisks eq 2) &
 (ds>>DSHAPE.nTracks eq 203 % ds>>DSHAPE.nTracks eq 406) &
 (ds>>DSHAPE.nSectors eq 12 % ds>>DSHAPE.nSectors eq 14)
   ifso
      [
      sysDisk>>BFSDSK.nDisks = ds>>DSHAPE.nDisks
      sysDisk>>BFSDSK.nTracks = ds>>DSHAPE.nTracks
      sysDisk>>BFSDSK.nSectors = ds>>DSHAPE.nSectors
      ]
   ifnot
      [
      if sysDisk>>BFSDSK.nDisks eq 2 then
         if TryDisk(1, 0, 0, 0)<<DST.finalStatus ne 0 then
            sysDisk>>BFSDSK.nDisks = 1
      if sysDisk>>BFSDSK.nTracks eq 406 then
         if TryDisk(0, 203, 0, 0)<<DST.finalStatus ne 0 then
            sysDisk>>BFSDSK.nTracks = 203
      if sysDisk>>BFSDSK.nSectors eq 14 then
         if TryDisk(0, 0, 0, 13)<<DST.finalStatus ne 0 then
            sysDisk>>BFSDSK.nSectors = 12
      ]

   [
   let curPart = eng gr 3? (table [ 61037b; 1401b ])(0), nil
   Puts(dsp, $*N); if eng gr 3 then PutTemplate(dsp, "Partition: $D, ", curPart)
   PutTemplate(dsp, "Disks: $D, Cylinders: $D, Heads: 2, Sectors: $D",
    sysDisk>>BFSDSK.nDisks, sysDisk>>BFSDSK.nTracks, sysDisk>>BFSDSK.nSectors)
   PutTemplate(dsp, "*NReady to scavenge a $S model-$S $Sfile system",
    (sysDisk>>BFSDSK.nDisks eq 1? "single", "dual"),
    (sysDisk>>BFSDSK.nTracks eq 203? "31", "44"),
    (eng gr 3? (sysDisk>>BFSDSK.nSectors eq 12? "12-sector ", "14-sector "), ""))
   if Confirm(" [confirm]") test (kbdAd!2 & 4000b) eq 0
      ifso [ EditDisk(); break ] ifnot resultis 0
   if eng gr 3 then 
      [
      let newPart = GetNumber("*NPartition number ", curPart)
      if newPart ne curPart & newPart ne 0 then
         [ (table [ 61037b; 1401b ])(newPart); break ]
      ]
   unless TryDisk(1, 0, 0, 0)<<DST.notReady do
      sysDisk>>BFSDSK.nDisks = Confirm("*NUse both disks?")? 2, 1
   unless TryDisk(0, 203, 0, 0)<<DST.seekFail do
      sysDisk>>BFSDSK.nTracks = Confirm("*NUse all 406 cylinders?")? 406, 203
   unless TryDisk(0, 0, 0, 13)<<DST.finalStatus eq badSector do
      sysDisk>>BFSDSK.nSectors = Confirm("*NUse all 14 sectors?")? 14, 12
   ] repeat
] repeat  // also valof

if sysDisk>>BFSDSK.nDisks eq 2 then WaitForDisk(1)
maxVDA = sysDisk>>BFSDSK.nDisks * sysDisk>>BFSDSK.nTracks *
 2 * sysDisk>>BFSDSK.nSectors -1
sysDisk>>BFSDSK.diskBTsize = maxVDA/16 +1
alterFlag = Confirm("*NMay I alter your disk to correct errors?")
Ws("*NProceeding with scavenge...")
]

//-----------------------------------------------------------------------------------------
and GetNumber(prompt, number) = valof
//-----------------------------------------------------------------------------------------
[
Ws(prompt)
PutTemplate(dsp, "$UO", number)
let digitTyped = false
   [
   let char = Gets(keys)
   switchon char into
      [
      case $*N: case $*S: case $*033:
         resultis number
      case $*177:
         Ws(" XXX"); resultis 0
      case $0 to $7:
         [
         unless digitTyped while number ne 0 do
            [
            SimpleDspEraseBits(dsp, -SimpleDspCharWidth(dsp, (number&7)+$0))
            number = number rshift 3
            ]
         number = number lshift 3 + char-$0
         Puts(dsp, char)
         digitTyped = true
         endcase
         ]
      case $*001: case $*010:
         [
         if number ne 0 then
            SimpleDspEraseBits(dsp, -SimpleDspCharWidth(dsp, (number&7)+$0))
         number = number lshift 3
         endcase
         ]
      ]
   ] repeat
]

//-----------------------------------------------------------------------------------------
and WaitForDisk(dsk) be
//-----------------------------------------------------------------------------------------
[
if TryDisk(dsk, 0, 0, 0)<<DST.notReady then
   [
   PutTemplate(dsp, "*NWaiting for drive $D to become ready...", dsk)
   until diskStatus>>DST.notReady eq 0 loop
   Ws("Ready!")
   ]
]