// Scan.Sr
//
// July 3 version
// Changes from previous version:
// 1) qBufRead is an outgoing static
// 2) scanstatus outgoing static. It is set to tcScanner
//	at the beginning of the Sretrty loop.

// Last modified October 29, 1979  7:29 PM by Taft

get "BRAVO1.dF"
get "altofilesys.d"
get "bfs.def"
get "q.df"
get "vm.df"

// Incoming procedures

external	[ 
	CallersFrame;
	TIMER
	InitializeCbStorage
	move
	errhlta
	GetCb
	LengthQ
	Dequeue
	DoDiskCommand
	waitforfd
	Enqueue
	ult
	umax
	ReadVec
	] 

// Incoming Statics

external	[ 
	DCread
	fillInDA
	DCseekOnly
	] 

// Outgoing Procedures

external	[ 
	ScanPages
	EnQBuf
	fCbWaiting
	] 

// Outgoing Statics

external	[
	scanstatus
	]

// Local Statics

static [
	scanstatus
	tcScanner
	tcUser
	cLoops
	cLoopsNoBuf
	Dbuf 
	]

// Local Structure

// Local Manifests


// S C A N P A G E S
//
let ScanPages(cfa,qBufFree,TcScanProc,bMpPgnDa,maxCRead,qBufRead; numargs na) = valof
[ let tqBufRead= vec lQ
switchon na into
	[ 
case 3:	bMpPgnDa = 0
case 4:	maxCRead = -1
case 5:  qBufRead = tqBufRead
case 6:	endcase
default: errhlta(212)
	] 
let pgnMac = umax(maxCRead,maxCRead+cfa >> CFA.fa.pageNumber)
qBufRead >> Q.head = 0;
let qBufReading = vec lQ; qBufReading >> Q.head = 0;
let scanParams = vec lScp;
scanParams >> SCP.fmCaller = CallersFrame()
scanParams >> SCP.qBufFree = qBufFree
scanParams >> SCP.qBufRead = qBufRead
scanParams >> SCP.fa = lv cfa >> CFA.fa
scanParams >> SCP.qBufReading = qBufReading
scanParams >> SCP.TcScanProc = TcScanProc
let zone=vec CBzoneLength
let fileId = vec 3
 tcScanner = tcNotDone      //insert "let"
 tcUser = tcNotDone      //insert "let"
 cLoops = 0;  cLoopsNoBuf = 0
InitializeCbStorage(zone, CBzoneLength, cfa >>CFA.fa.pageNumber, Sretry, true)
if bMpPgnDa ne 0 then
	[ bMpPgnDa! 0 = cfa >> CFA.fa.da
	zone>>CBZ.DAs=bMpPgnDa-zone>>CBZ.currentPage
	] 
zone>>CBZ.cleanupRoutine=EnQBuf
zone>>CBZ.currentNumChars=#1000
zone>>CBZ.currentDA=cfa >> CFA.fa.da
zone>>CBZ.errorRoutine=ReQBufs
zone>>CBZ.extra=scanParams
fileId ! 0 = cfa >> CFA.fp.version;
move(lv (cfa >>CFA.fp.serialNumber),fileId+1,2);

Sretry:
[
// Note that each cb is used twice:  to hold the DL for
// page i-1, and then to hold the DCB 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.
let pgn = zone>>CBZ.currentPage; let da = zone>>CBZ.currentDA

let cb=GetCb(zone)
let nextCb = 0
let FReturn = fCbWaiting

	[ cLoops = cLoops+1
	scanstatus = tcScanner
	tcUser = (scanParams >> SCP.TcScanProc)(zone,FReturn)
	if (tcUser eq tcDone) % (tcUser eq tcAbort) %
	   ((tcScanner eq tcDone) & (tcUser eq tcToYou)) then
		break
	if nextCb eq 0 then
		[ if fCbWaiting(zone) eq false then
			loop
		nextCb = GetCb(zone)
		] 
	if zone>>CBZ.currentNumChars ne #1000 then
		[ tcScanner = tcDone
		FReturn = FNever
		loop;
		] 
	if pgn eq pgnMac then
		[ test rv zone>>CBZ.queueHead eq 0 ifso
			[ tcScanner = tcDone
			FReturn = FNever;
			] 
		ifnot	nextCb = 0
		loop
		] 
	let buf = Dequeue(qBufFree)
	Dbuf= buf             //debug static
	if buf eq 0 then
		[ FReturn = FBufAvail;
		cLoopsNoBuf = cLoopsNoBuf+1
		loop
		] 
	FReturn = fCbWaiting
	Enqueue(qBufReading,buf)
	cb>>CB.labelAddress=lv nextCb>>CB.diskAddress

	DoDiskCommand(cb, buf >> BUF.ca, da, fileId, pgn, DCread)

	cb=nextCb; nextCb = 0;
	da = fillInDA; pgn = pgn+1;
	] repeat

// This is what was here before.  It's not safe, since the current cb
// (located in this procedure's stack) may still be executing!!!
// rv nextDiskCommand = 0

// Here is what we do instead: cut off the chain, then wait for
// the current cb to be finished.
	[
	cb = @nextDiskCommand
	if cb eq 0 then break
	cb>>CB.nextCommand = 0
	] repeat
]
if tcUser eq tcAbort then resultis tcAbort
if tcUser eq tcDone then resultis tcByScanProc
// else
resultis tcByScanPages
]

// E N Q B U F
//
and EnQBuf(cb) be
[ if cb >> CB.command eq DCseekOnly then return
let zone = cb >> CB.zone; let label = cb>>CB.labelAddress
if zone >> CBZ.DAs ne 0 then
	(zone >> CBZ.DAs) ! (cb >> CB.truePageNumber+1) = label>>DL.next
let scanParams = zone >> CBZ.extra
let buf = Dequeue(scanParams >> SCP.qBufReading)
buf >> BUF.pgn = label >> DL.pageNumber
buf >> BUF.numChars = label >> DL.numChars
let tq = (buf >> BUF.pgn eq 0) ? scanParams >> SCP.qBufFree,scanParams >> SCP.qBufRead
Enqueue(tq,buf)
let fa = (zone >> CBZ.extra) >> SCP.fa
unless (fa >> FA.pageNumber eq label >> DL.pageNumber) % (fa >> FA.pageNumber+1 eq label >> DL.pageNumber) then
	errhlta(213)
fa >> FA.pageNumber = label >> DL.pageNumber
fa >> FA.da = cb >> CB.diskAddress
fa >> FA.charPos = label >> DL.numChars
zone >> CBZ.currentDA = label >> DL.next
] 

// F C B W A I T I N G
//
and fCbWaiting(zone) = (((@(zone >> CBZ.queueHead)) >> CB.status & DSdoneBits) ne 0)

// L F I LE 
//
// and LFile(cfa) be
// [ let ca1 = vec #400; let ca2 = vec #400;
// let buf2 = vec lBuf; buf2 >> BUF.ca = ca2;
// let buf1 = vec lBuf; buf1 >> BUF.ca = ca1;
// let qBuf = vec lQ
// qBuf >> Q.head = 0;
// Enqueue(qBuf,buf1); Enqueue(qBuf,buf2)
// ScanPages(cfa,qBuf,tcReQ)
// ] 

// R E Q
//
// and tcReQ(zone,fReturn) = valof
// [ let scanParams = zone >> CBZ.extra;
// let qBufRead = scanParams >> SCP.qBufRead
// until LengthQ(qBufRead) eq 0 do
// 	Enqueue(scanParams >> SCP.qBufFree,Dequeue(qBufRead))
// resultis tcToYou
// ] 

// R E Q B U F S
//
and ReQBufs(zone) be
[ let scanParams = zone >> CBZ.extra;
let qSrc = scanParams >> SCP.qBufReading
until LengthQ(qSrc) eq 0 do
	Enqueue(scanParams >> SCP.qBufFree,Dequeue(qSrc))
] 

// F B U F A V A I L
//
and FBufAvail(zone) = (LengthQ((zone >> CBZ.extra) >> SCP.qBufFree) eq 0) ? false,true

and FNever(zone) = false