// DiExRun.bcpl

//	Last modified October 26, 1977  9:43 AM

get "DiEx.defs"

static [ pass; totalPasses; WritePass; ErrRes=3 ]
static [ @RandomAddr = #123456; @nPages ]  //local statics
structure BIT[ b ↑ 0,256*16 bit ]

let RunDisk() be
	[
	Msg(" Testing .....")
	let saveY,saveX = @mouseY,@mouseX
	let repeats = T>>P.Repeats
	let listConstant = true
	PattCnt = 1
	if (@diskStatus & DSTerrorBits) ne 0 then ResetDisk(true)
	let cleanUp = (T>>P.TestData % T>>P.ListPage)? cleanUpRoutine,0
	let PagesPerCylinder = nHeads*nSectors
	let mouseXinc,mouseXposn = (500/repeats),0
	Zero(DAs,100)
	if T>>P.StopDisplay then [ rv DAstart = 0; Wait(50) ]
	saveMsgS = MsgS; if not T>>P.MsgEnable then MsgS = 0
	let LastPage = nil
	nPages=makeDAtable(0)  //initialize nPages to selected heads times selected sectors
	pass = 0
	let dummy = vec 1
	let cylindersRead = 0
	[passLoop
	for dr = FirstDr to LastDr do
	[driveLoop
	mouseXposn = mouseXinc * (pass+1)
	rv mouseX = mouseXposn
	DiskSel = dr
	SetParameters()


	if (T>>P.Waction.header ne diskSkip) & WriteEnable then 
		[ WritePass = true
		SetCursor($W)
		makePattern(wbuff,1000)
		for cylinder = T>>P.FirstCyl to T>>P.LastCyl do
			[
			rv mouseY = cylinder*2
			rv mouseX = mouseXposn
			makeDAtable(cylinder)
			LastPage = 0
			for i=0 to nPages do CAs!i = WhereIsWbuff(i)
			
			DoWrite:
			test T>>P.Chain
				ifso LastPage = ActOnPages(CAs, DAs, 0, LastPage+1, nPages, T>>P.Waction)
				ifnot for p = 1 to nPages do
					[
					LastPage = ActOnPages(CAs, DAs, 0, p, p, T>>P.Waction)
					//if LastPage ne p then break
					if SkipOperation() then goto exit  
					]
			if SkipOperation() then goto exit  
			if LastPage ls nPages then goto DoWrite
			cylinder = cylinder + T>>P.CylInc-1
			]
		]


	WritePass = false
	cylindersRead = 0
	for i=1 to PagesPerCylinder by blks do 
		for n = 0 to blks-1 do CAs!(i+n) = rbuff + (n*300)
	if (T>>P.Raction.header ne diskSkip) then
		[
		SetCursor($R)
		for cylinder = T>>P.FirstCyl to T>>P.LastCyl do
			[
			rv mouseY = cylinder*2
			rv mouseX = mouseXposn
			makeDAtable(cylinder)
			LastPage = 0
			DoRead:
			test T>>P.Chain
				ifso LastPage=ActOnPages(CAs, DAs, 0, LastPage+1, nPages, T>>P.Raction, 0, 0, 0, cleanUp)
				ifnot for p = 1 to nPages do
					[ LastPage = ActOnPages(CAs,DAs, 0, p, p, T>>P.Raction, 0, 0, 0, cleanUp)
					//if LastPage ne p then break
					if SkipOperation() then goto exit  
					]
			if SkipOperation() then goto exit  
			if LastPage ls nPages then goto DoRead
			cylindersRead = cylindersRead+1
			cylinder = cylinder + T>>P.CylInc-1
			] 
		] 


	let CylRange = ((T>>P.LastCyl - T>>P.FirstCyl)/T>>P.CylInc)+1
	if T>>P.nRandom then
		[
		SetCursor($R)
		for i = 1 to T>>P.nRandom do
			[
			RandomAddr = MakeRandom(wbuff,2,RandomAddr)
			let nextCyl = T>>P.FirstCyl + ((wbuff!0&#7777) rem CylRange)*T>>P.CylInc
			rv mouseY = nextCyl*2
			rv mouseX = mouseXposn
			makeDAtable(nextCyl)
			let p = 1+(((wbuff!1)&#177) rem nPages)
			let Cmmd = 0; Cmmd<<ACTx.header = diskCheck
			ActOnPages(CAs,DAs, 0, p, p, Cmmd)
			if SkipOperation() then goto exit  
			]
		]
	if cylindersRead then
		[
		FAD(dr,FML(FLDI(30,nPages),FLDI(31,cylindersRead))); cylindersRead = 0
		FAD(dr,FLDI(31,T>>P.nRandom))
		]
	if SkipOperation() then goto exit  
	]driveLoop
	totalPasses = totalPasses + 1; pass = pass+1
	if pass ge 500 then pass = 0
	if pass eq repeats then break 
	if listConstant eq true then
		listConstant = ((cylindersRead*(FirstDr+1 - LastDr)*(T>>P.LastHd+1 - T>>P.FirstHd))/41)
	if listConstant then if totalPasses rem (1000/listConstant) eq 0 then ListET(true)
	]passLoop repeat


	exit:
	@mouseX = saveX; @mouseY = saveY
	FAD(DiskSel,FML(FLDI(30,nPages),FLDI(31,cylindersRead)))
	rv DAstart = displayCB; MsgS = saveMsgS
	ListET(true)
	Msg(" Done*n*n>>")
	]

and cleanUpRoutine(cb,doneOK) =valof
	[ let res = false
	if (T>>P.TestData eq 1)%((T>>P.TestData eq 2) & not doneOK) then res=CheckData(cb,true)
	if (T>>P.ListPage eq 1)%((T>>P.ListPage eq 2) & not doneOK) then PrintContents(cb)
	resultis res
	]

and makeDAtable(Cylinder) = valof
	[
	let nPage = 0
	let DA = 0
	for head = T>>P.FirstHd to T>>P.LastHd do
		for sector = T>>P.FirstSec to T>>P.LastSec do
			[ nPage=nPage+1
			DA<<DA.track = Cylinder; DA<<DA.head = head; DA<<DA.sector = sector
			DAs!nPage = DA
			]
	resultis nPage
	]

and SkipOperation(stream) = valof
	[
	unless CheckInput() then resultis 0  
	MsgS = saveMsgS
	rv DAstart = displayCB
	PrintBits()
	Msg("  ... Continue?"); GetChar(); char = GetChar()
	let result = (char eq $N) % (char eq Middle) 
	Msg(" $S",result? "No ... ","Yes")
	unless result then RestartDiex()
	resultis result 
	]

and CheckData(cb,QuickCk) = valof
	[
	let buffw = WhereIsWbuff(cb>>CB.truePageNumber)
	let buffr = cb>>CB.dataAddress
	manifest errlines = 20
		//buffr!10 = 50				//~~~~~~~~~~~~~USED TO TEST THIS CODE
	let cnt = quickCheck(buffw,buffr,wordsPerPage)
	if not cnt then resultis 0 
		//[ Zero(buffr,wordsPerPage); resultis 0 ]
	if QuickCk then resultis cnt
	if cnt gr thisErr>>ET.burstBits then thisErr>>ET.burstBits = cnt
	if CheckInput() then resultis 0  
	MsgS = saveMsgS
	if cnt then Msg("*n$4D errors found by checking memory after read!",cnt)
	cnt = 0
	let lasterror,ErBurst = wordsPerPage,0
	for i = wordsPerPage-1 to 0 by -1 do if buffw!i ne buffr!i then 
		[
		test i eq lasterror - 1
			ifso ErBurst = ErBurst + 1
			ifnot
				[
				if ErBurst gr errlines then Msg("*n  ETC. for $D consecutive errors.",ErBurst)
				ErBurst = 0
				]
		lasterror = i
		if ErBurst le errlines then
			[
			if i & (ErBurst eq 0) then Msg("*n OK Word $4D was $6F0UO.", i+1, buffr!(i+1))
			Msg("*n    Word $4D: wrote $6F0UO, but read $6F0UO; error = $6F0UO.", i, buffw!i, buffr!i, buffw!i xor buffr!i)
			]
		cnt = cnt+1
		]
	if ErBurst gr errlines then Msg("*n   ETC. for $D consecutive errors.",ErBurst)
	//Bug("undetected errs",cb,cnt)
	if cb>>CB.command.dataAction eq diskCheck then Zero(buffr,wordsPerPage)
	RestartDiex()
	resultis cnt
	]

and RestartDiex() be
	[ if T>>P.StopDisplay then rv DAstart = 0; Wait(50)
	 if not T>>P.MsgEnable then MsgS = 0
	]

and quickCheck(wrote,read,cnt) = valof
	[ external CompairASM
	let buff = vec 2
	buff!0 = wrote
	buff!1 = read
	buff!2 = cnt
	resultis CompairASM(buff)
	]

and SetParameters() be
	[
	ErrRes  = T>>P.ErrRes
	maxEC  = T>>P.maxEC
	//if TriexHandeling then
		//[ SetBlock(HeaderBuff,3,32)
		//LabelBuff!0 = #100000%DriveSel; for i = 1 to 31 do LabelBuff!i = i
		//]
	]

and ErrorLength(wrote,cb) = valof
	[
	let buffw = WhereIsWbuff(cb>>CB.truePageNumber)
	let buffr = cb>>CB.dataAddress
	let Fw,Fb,Lw,Lb = 0,0,0,0
	for w = 255 to 0 by -1 do for b = 0 to 15 do
		[ let wb = w+b
		if buffw>>BIT.b↑wb ne buffr>>BIT.b↑wb then [ Fw=w; Fb=b; break ]
		]
	for w = 0 to Fw do for b = 15 to 0 by -1 do
		[ let wb = w+b
		if buffw>>BIT.b↑wb ne buffr>>BIT.b↑wb then [ Lw=w; Lb=b; break ]
		]
	let length = (Lw-Fw)*16 + Lb-Fb + 1
	resultis length
	]

// from page number figure out where Wbuff must be
and WhereIsWbuff(pageNumber) =valof
	[
	test PattCnt eq 2
		ifso resultis wbuff+((wbuff!pageNumber) & #377)
		ifnot resultis wbuff
	]