// P R I N T   
// errors 5000
//

get "SprucePrinters.d"
get "SpruceDoc.d"
get "SpruceFont.d"
get "SpruceBand.d"
get "spruceMisc.d"
get "SpruceFiles.d"
get "Orbit.d"

// outgoing procedures
external
	[
	DrivePrinter
	]

// incoming procedures
external
	[
	SpruceError
	Max; Min
	CallSwat
	InitRam
	ChooseMailboxBin
//SprucePrint
	AddToBin
	Announce
	AwaitPageSync
	IncBinSerial
	PrintNext
	ReadBands
	ReadClock
	ReadFont
//CURSOR
	CursorChar; CursorDigit
// SprucePrintRes
	DoFunc; ROSStatus
// Timer (debug only)
	SetTimer; TimerHasExpired
	]

// incoming statics
external
	[
	BandFile
	bandRecordSize
	BinCounters
	breakPage
	breakPageCopy
	Capabilities
	Debug
	DebugSystem
	debugTrail
	FAvalue
	Func
	knockResult
	logBandRecordSize
	LowAdr
	Measure; CloseMeasure; TickMeasure; InitMeasure; measureTable
	nBands
	nPagesPrinted
	nRecs
	numMustPrint
	numPrinted
	printerForward
	ros12
	ros13
	scanTicks		// # of 38 usec. ticks in 4 printer scan lines.
	stopsPrinting
	xmFonts	// Font data will be read into Bank 1 via Bank 0, if set
	BinSerials
	]

// internal statics
static
	[
	imageTimeout
	rosCount = 0 // Pimlico/puffin/penguin command count
	printSeq 
	twosided
	curBin; nextBin
	curHandling; nextHandling
	switchBin = false	// for delayed bin switch if intermediate break page needed
	nextCopy
	debDelay = 0 // debugging: delay per page to simulate printing. 
	]

// File-wide structure and manifest declarations.

manifest [
	AuxCount = 19		//keep count of sheets in Aux tray in BinCounters!19
	AuxRecoveryCount = 20 	//number of first sides to be replaced in BinCounters!20
	sigband = 100
	RTC=#430
	eTicks = 4		//time to spend checking scanning
	fITO = 15*27		//15 seconds to get first sheet thru
	rITO = 2*27		//2 seconds to get subsequent sheets thru.
	timeOut=15*27  //time to wait for standby status to appear
	printHysteresis = 30 // print this many before allowing suspension, each time in
	debugTrailSize = 10*3
	minDocumentError = 300 // Document inherently hard to print
// commands to Penguin
	pResetCount = #60000	// 6000x- reset your count of commands received from me
	pNewLED = #62400		// 65nmx- last 2 nibbles contain decimal digits
	pSetLED = #63002		// 6602x- display last value sent with pNewLED
	pPrint = #63001			// 6601x- print a page
	pSSMain = #63040		// 6620x- page is singlesided from main tray
	pSSAux = #63041		// 6621x- page is singlesided from auxtray
	pTSFirst = #63042		// 6622x- page is first side of duplex (goes from main to aux)
	pTSSecond = #63043		// 6623x- page is second side of duplex (aux to some output bin)
	pSetBin = #63100		// 6640x- position bins (to bin indicated in last five bits)
	pClearMalf = #63050	// 6628x- move malf indication to  status
	pIgTSCErr = #63004		// 6004x- ignore tsc error indication
// printSeq values:
	warmup = 1
	standby = 2
	preprint = 3
	printing = 4
	cycledown = 5
	malfClear = 6
	mainSeqErr = 107
	subSeqErr = 106
	abnormal = 113
	powerOn = 123
		]


// -----------------------------------------------------------
// Print		Initialization
// -----------------------------------------------------------

// Returns -1 if printing is successfully completed, else nPagesPrinted, so can pick up where we left off.

let DrivePrinter(pDoc, nPagesAlreadyPrinted,lvFailureCode) = valof
[
	let measureVec = vec 5
	if (DebugSystem & #4000) ne 0 then CallSwat("DrivePrinter")
	unless nPagesAlreadyPrinted then BinCounters!AuxCount = 0  // if no pages printed, shouldn't be paper in aux tray
	let CommTab=vec 8  
	CommTab=(CommTab+1)&(-2)

	let knockKnock = false		// time to let somebody in if true
	twosided = pDoc>>DocG.duplex ne 0
	curBin = 0
	let overflowBin = 0		// usually is . . .
	test (Capabilities & mMailbox) ne 0
	ifso
		[
		unless BinCounters!0 > 50 do  // can't move trays  
			[  //find the bin we would like it to go in
			let mtray = ChooseMailboxBin((lv pDoc>>DocG.CreatStr)>>STR.char↑1)
			if BinCounters!mtray <50 do curBin = mtray  // if there is room
			]
		breakPageCopy=curBin?1001,1002
		]
	ifnot
		[
		if (Capabilities & mMultiBin) ne 0 do  //filling bins from bottom up - find 1st nonfull 
			[ until  (BinCounters!(curBin) < 50) % (curBin eq 18) do
				 [ curBin = curBin + 1 ]
			overflowBin = 18 
			]
		]
	let binMax = (curBin eq overflowBin)? 200, 50

	InitializeHardware()
	InitMeasure(measureTable, 2000, measureVec)
	let backup = 0
	let xportModel = 0
	@lvFailureCode =  (ROSCommand(pResetCount) % ROSCommand(pIgTSCErr))? 121, 0
	@lvFailureCode = ROSCheck()
	if @lvFailureCode resultis SetupRecovery(pDoc, lvFailureCode, lv xportModel, nPagesAlreadyPrinted)
	let  needFonts, needInit =true, true
	let  FontTable, nfRecs, LeftOver, LeftOverGuard =  nil, nil, nil, nil
	let buf = nil
	let  tim =  nil
	imageTimeout = fITO  //allow penguin to get ready
	if BinCounters!AuxRecoveryCount do nPagesPrinted = BinCounters!AuxCount
	let nextPage=NextSheet(pDoc, nPagesPrinted)
// Print (cont.)		 Loop

[PrintLoop
	// Find out which page to print next:
	let page=nextPage
	if page eq 0 then [ knockKnock = false; IncBinSerial(BinSerials+curBin); break ]
			 // Finished!
	if knockKnock then break // go let somebody in

	if needFonts do	// Read in font and relocate the pointers to characters:
		[
		let font=page>>PageG.fontLoad*(size FontG/16)+pDoc>>DocG.Fonts
		FontTable = ReadFont(font, LowAdr, nRecs)	//  call returns location of ICC table
		unless xmFonts do for i=0 to pDoc>>DocG.ICCtotal-1 do // if rasters are in bank 1, they start at 0
			FontTable!i=FontTable!i + LowAdr
		FontTable=FontTable-100000b

		// Figure out where buffers are.
		let nRecords= xmFonts? (pDoc>>DocG.ICCtotal+bandRecordSize-1) rshift logBandRecordSize,
				font>>FontG.nRecords
		nfRecs=nRecs-nRecords // max allowable band list size 
		LeftOver=LowAdr+(font>>FontG.nRecords lshift logBandRecordSize)
		LeftOverGuard = LeftOver+(loSize&(-4))-4
		buf = (LeftOver+loSize+8)&(-2) // ~~ all the 4's, 8's and 10's in here are cowardly slack
		needFonts = false
		]

	// Now read in the bands for this page
	ReadBands(page, buf, nfRecs, nBands)

	if needInit  do  	// if this is the first time thru or paper handling changed
		[
		if stopsPrinting eq binFull then [ @lvFailureCode = 130; break ]
		// Initialize the Orbit,  etc.
		InitializeHardware()
		// wait for ready or some serious condition or for long enough
		tim = @RTC
		unless Debug do
			[
			let x = ROSCheck(standby)
			unless x then break // ready
			if x > 0 then resultis SetupRecovery(pDoc, lvFailureCode, lv xportModel, nPagesPrinted, x) 
			//result was -1:  not ready yet, but may be soon.  Have we waited long enough?
			if (@RTC - tim) > timeOut resultis SetupRecovery(pDoc, lvFailureCode, lv xportModel, nPagesPrinted, 120)
			] repeat


		xportModel = 0		// paper cleared from transport mechanism
		// tell penguin to print a page and how to handle the paper
		curHandling = nextHandling
		let bin = nextBin
		test (nextBin eq overflowBin) & (curBin ne overflowBin) then	// switching to overflow bin:  do break page
			[ nPagesPrinted = nPagesPrinted - 1; binMax = 200; bin = curBin;switchBin = true ]
			or [ switchBin = false ]
		if ROSCommand(pSetBin + 2+bin) %  ROSCommand(curHandling) % ROSCommand(pPrint) do
			[ @lvFailureCode = 122; break ]

		curBin = nextBin

		// Try to empty Orbit buffers to clear them.  This is to minimize toner dumping on first (blank) page.
		compileif loSize ls 256 then [ foo=nil ]
		for i=0 to 31 do DoFunc(fReadBlock, 256, LeftOver)

		needInit = false
		]


// -----------------------------------------------------------
// Print		real printing function
// -----------------------------------------------------------

	// first, set up to display page number we're printing on penguin's led
	nPagesPrinted=nPagesPrinted+1
	let  m,n = nil, nil
	m, n, m = nPagesPrinted rem 100, m rem 10, (m/10) lshift 4

	//and build table so orbit will send them thru during page imaging
	CommTab!0 =  180			
	CommTab!1 = #62400 + m +n		//set up leds and
	CommTab!2 = 150
	CommTab!3 = #63002		//display'em
	CommTab!4 = -1			// this will be set to an appropriate band number if there is another page
	CommTab!5 = #63001		//command to print another page
	CommTab!6 = -1		

	// Start Orbit working on the present page.
	DoFunc(fControl, #21)		//Reset, clear behind
	DoFunc(fROSCommand, adBufferReset)

	LeftOver!0=0			//Initialize LeftOver table
	LeftOverGuard!1=-1
	tim=@RTC

	// Start the microcode!!!!
	DoFunc(fSlot, CommTab, 20000, nBands-1, FAvalue, LeftOver,
			FontTable, buf-1, nil, nil, (nextCopy lshift 5)+4)
	if Debug then [ while (@RTC-tim) < debDelay loop; tim = @RTC ] // ~~delay
	
// Assume the page we are printing will be OK and prepare for whatever comes next:

	rosCount = rosCount + 2		// for the led setting commands
	CursorDigit(nPagesPrinted)

	// See who's (maybe) knocking at my door: if we are not planning to defer to spooler,
	//  don't trouble to shut down here, just to find that out there. Would be too expensive
	if (DebugSystem&#30) eq 0 & numPrinted ge numMustPrint & knockResult &
		(nPagesPrinted-nPagesAlreadyPrinted) > printHysteresis 
	 then knockKnock = true

	// Find next page to work on:
	nextPage=NextSheet(pDoc, nPagesPrinted)

	needInit = true
	if (not knockKnock) & nextPage &   //if nobody waiting and there is more to do
		(nextBin eq curBin ) & (nextHandling eq curHandling ) &   // and penguin doesn't need a pause
		(not switchBin) & (stopsPrinting eq false) then	// and no forced bin switch then continue printing.
							// enable interband communication with penguin to set up the next revolution
							// keep track of what I've sent so we don't get out of sync
		[
		needInit = false
		CommTab!4 = sigband
		rosCount = rosCount + 1 
		]
	needFonts = (nextPage>>PageG.fontLoad ne page>>PageG.fontLoad) & (nextPage ne 1)


// Now see if we actually printed this properly.  Wait for Orbit to announce it is finished.

	while @#720 ne 0 do if (@RTC-tim) gr imageTimeout then
		[
		InitializeHardware()		// clear hardware, #720
		 // Machine dead or Orbit got behind and it wasn't detected
		 // Will generally be overriden by more specific analysis
		@lvFailureCode = 103 // Manual intervention required
		break
		]
	// Check Orbit status
	let nonF = 0
	let stat=Func!9		//Orbit printing status.
	// Fatal error detected by microcode -- invalid Band entry -- possibly broken processor
	if stat<<STATUS.invalidBandEntry then
		[ InitializeHardware(true); 
		  if LeftOverGuard!1 eq -1 then SpruceError(1010, @1, @2)  //else, assume that
			//bad band entry is simply result of leftover overflow
		]
	test stat<<STATUS.prematurePageAbort then [ Announce(16); nonF = 301 ]
	  or if stat<<STATUS.behind then [ Announce(16); nonF = 300 ]
	if stat<<STATUS.timeout then [ Announce(17); @lvFailureCode = 102 ]
	if LeftOverGuard!1 ne -1 then
	  [ Announce(18); nonF = 310 ]  // then check possible next page data enclobberment
	if nonF then @lvFailureCode = nonF

	// Now try for more specific engine status check
	let running = needInit?cycledown, printing
	let printingSecondSide = (running eq printing) & (curHandling eq pTSSecond)
		tim = @RTC
		unless Debug do
			[
			let x = ROSCheck( running, printingSecondSide)
			unless x then break // that's what we expected
			if x > 0 do [@lvFailureCode = x ; break ] 
			//result was -1:  ok if cycledown expected and still printing
			if  (running eq printing) % ((@RTC - tim) > 27) do 
				[
				@lvFailureCode = 114; break
				]
			] repeat
	

	// Now wait for page sync to go off
	unless AwaitPageSync(0) then @lvFailureCode = 101

//Now decide what to do:
	if (@lvFailureCode > 0) & (@lvFailureCode < minDocumentError) do [ backup = 1; break ]

	imageTimeout = rITO	+(needInit?11*27,0)	// next image should be out in less than 2 seconds
	xportModel = ((xportModel & 3) lshift 1) + 1
	test curHandling eq pTSFirst then
		[ 
		BinCounters!AuxCount = (BinCounters!AuxCount) +1
		if BinCounters!AuxRecoveryCount do
			[
			BinCounters!AuxRecoveryCount = BinCounters!AuxRecoveryCount -1
			unless BinCounters!AuxRecoveryCount do nPagesPrinted = nPagesAlreadyPrinted
			] 
		]
		or 		// note that bin counters not incremented if filling aux tray
		[
		 AddToBin(binMax, curBin); if  (curHandling eq pTSSecond) do [ BinCounters!AuxCount = (BinCounters!AuxCount) - 1 ] 
		]
]PrintLoop repeat

	InitializeHardware()
	CloseMeasure()
	resultis (knockKnock % @lvFailureCode)? SetupRecovery(pDoc,lvFailureCode,lv xportModel, nPagesPrinted-backup), -1
]
// --------------------------------------------------------------------------
// code = ROSCheck(expectedPrintSeq)
// --------------------------------------------------------------------------

// analyze ROS status
// code = 	0 if there are no malfunctions, ROS is ready to print
//			-1 if the only thing wrong is that penguin needs to cycle round to the expected state
//			otherwise, a code for the error detected
//			
// expectedPrintSeq	where caller thinks penguin should be(printing, cycling down, standing by, don't care)

// ROSCheck first screens the current status for any malfunction indications, and
// returns right away if there are none. Otherwise, it applies the current status
// against a printer-dependent table of possible conditions, yielding a failure
// code. 
// A check verifies that the laser is on and the polygon is
// scanning (SOS/EOS are seeing things), by making sure that the low four bits of the line
// count (indicated in status word) are  changing. The code reads the line count, waits
// approximately the time needed for four scan lines (generous margin), then reads the
// count again, reporting a problem if the values are equal. This test happens only when
// the running parameter is false. The wait time (scanTicks) is computed in PrintInit, as:

// ResolutionS is scaled by 10 as stored, PaperSpeedInches by 100. The (fast 10 bits of)
// the RTC ticks at 38 usec. intervals. Thus the # ticks wanted for four lines is:

//   ( 4 lines x 10↑6 usec/sec x  1/38 tick/usec.) / ((PaperSpeedInches/100 x ResolutionS/10) lines/sec)

// or about (106 x 10↑3) / (PaperSpeedInches/100 x ResolutionS/10)   ticks -- about 30 for Dover.

// This is only approximate because ROSStatus time, about 10 ticks for Dover, is not included.
// Part of the reason for waiting four line times is to account for possible delays in obtaining
// the correct line count status. Later, this method can be used to check for correct polygon speed.
// The scanTicks static is shifted left by 6 to match the clock reading function's results

and ROSCheck(expected, printingSecondSide; numargs na) = valof

[
if na < 2 then printingSecondSide = false
ros12 = ROSStatus(12)
ros13 = ROSStatus(13)
let  scanning, auxTrayProblem = true, false
let malFnStatus = ros13&#77
let auxTrayEmpty = (ros13 & #100) ne 0
let mainTrayEmpty = (ros13 & #200) ne 0
let subSeq = (ros13 rshift 8)&7
let mainSeq = (ros13 rshift 11)&7
let mode = ros13 rshift 14
let printSeq = selecton mainSeq into
	[
	case 1: powerOn
	case 2: warmup
	case 3: standby				 
	case 4:  0
	case 5: malfClear				 
	default: mainSeqErr						//Mainseq error
	]
unless na do expected = printSeq  // printing not valid state if nothing expected
unless printSeq do [ printSeq = selecton subSeq into
	[
	case 0: printing			 
	case 1: preprint			 
	case 2: cycledown 
	case 4: abnormal
	default: subSeqErr					// subSeq error
	] ]
let ready = 
	(printSeq eq expected) & // penguin is where we think it should be
	(malFnStatus eq 0) &
	(not mainTrayEmpty) &
 	((ros12 & #12377) eq (rosCount & #377)) & //no malf and no queue problems
	mode eq 1 // and printer is in remote print mode

if ready & (printSeq ne printing) then
	[
	  let lineCount = ROSStatus(7) & #170000
	  let rtc = ReadClock()
	  until (ReadClock()-rtc) ge scanTicks loop
	  scanning = (ROSStatus(7)&#170000) ne lineCount
	if expected ne cycledown do	// can't check while paper may be moving
		[  
		if (BinCounters!AuxCount eq 0) & (not auxTrayEmpty) then auxTrayProblem = 72
		]
	]
if printingSecondSide & auxTrayEmpty then auxTrayProblem = 70

if ready & scanning & (auxTrayProblem eq false) % Debug resultis 0

//  identify the problem or condition preventing ready

let malf =  selecton malFnStatus into [
	case 1: 81		// processor interlock open
	case 2: 82		// zone IIA jam
	case 3: 83		// zone IIB jam
	case 4: 84		// zone III jam
	case 5: 85		// sorter interlock open
//	case 6: 86		// stop print push button pressed: not used in remote print.
	case 7: 71		// aux tray empty	
	case 8: 115		// main tray empty - shouldn't happen with mainTrayProblem being watched.
	case 9: 108		// ink low
	case 10: 109	// trays don't match
	case 11: 110	// fuser not ready
	case 12: 111	// main tray paper misfeed
	case 13: 80		// aux tray paper misfeed
	case 14: 112	// cycle control state error
	case 15: 124	// paperexit jam
	case 16: 125	// (output) bin enter jam
	case 0: 0  
	default : -1  ]
let noResponse = EngineCommErr(ros12)	//check status of spruce communication with penguin
let code = 0
	test mode ne 1 then code = 100	// local mode - Penguin not listening
	 or test ros12 < 0 then code = 104	// wait state - Penguin not listening
	 or test malf > 0 then
		 [
		 let x = ROSCommand(pClearMalf)
		 unless (auxTrayProblem eq 70) do code = malf	// good malfunction code
		 ]
	 or test noResponse then code = 116	// penguin not talking
	 or test printSeq > malfClear then code =printSeq	//  State error
	 or test malf then code = 117		// unknown malfunction code
	 or test not scanning then code = 118	// laser appears to be off
	 or test auxTrayProblem then code = auxTrayProblem
	 or test mainTrayEmpty then code = 132
	 or test printSeq ne expected then code = -1
	 or code = 119	// can't figure out what's wrong
resultis code
]
// NextSheet(pDoc, afterPage)
// -----------------------------------------------------------------------------------
// Setup paper handling info, copy number and pointer to PageG structure that describe the next image.  
// It is assumed that the PageG entities represent consecutively numbered pages.  whichSide identifies a 
// page as 'odd' or 'even'; 'odd' pages face the same way the break page does --up, in the normal penguin mode [down, if "printerForward"].  
// All pages are paired except:  the break page, the first page if it is "even", the last page if it is "odd"
// NextSheet makes two passes thru the document:
// ** on the first pass all images that will be facing down are printed and stored in the aux tray  in reverse order.
// **on the second pass the pages that will be face up in the output bin are printed. This pass is complicated 
//	by the fact that there may be face-down sheets for which there is no corresponding face-up image 
//	-- these sheets must be passed thru at the appropriate time -- and by the fact that there may be face-up sheets for which 
//	there is no corresponding face-down sheet -- these sheets must be fed from the main tray instead of the aux.

and let NextSheet(pDoc, afterPage) = valof   [
nextBin = SelectBin(curBin)
if (nextBin eq 0) & (curBin ne  0) & breakPage & (afterPage < pDoc>>DocG.nPages) do  
	// switching to overflow bin - leave break page in mailbox
	[ nextHandling = pSSMain; nextCopy = 1011; breakPageCopy = 1012
	IncBinSerial(BinSerials+curBin)
	resultis ((pDoc>>DocG.nPages)-1)*(size PageG/16)+pDoc>>DocG.Pages ]
let nPages, nBreaks = pDoc>>DocG.nPages, breakPage
unless twosided  & ((nPages - nBreaks) gr 0) do
	 [ nextHandling = pSSMain; resultis PrintNext(pDoc, lv nextCopy, afterPage) ]
nPages = nPages - nBreaks
let bkPage = nPages*(size PageG/16)+pDoc>>DocG.Pages  // if there is one
let nCopies = pDoc>>DocG.nCopies
let page = pDoc>>DocG.Pages		// pointer to first page
let firstPageEven = (page>>PageG.whichSide eq 0)? 1,0
page = (nPages-1) * (size PageG/16) + page		//  pointer to last page
let lastPageOdd = (page>>PageG.whichSide ne 0)? 1,0
let nDoubles = (nPages - firstPageEven - lastPageOdd)/2
let nSheets = nDoubles + firstPageEven + lastPageOdd
let nAuxSides, auxIncr, nAuxBreaks, incr, relImageIndex, copy = nil, nil, nil, nil, nil, 0
test printerForward ifso
  [ nAuxSides, auxIncr, nAuxBreaks, incr, nBreaks = nDoubles + lastPageOdd, lastPageOdd, nBreaks, firstPageEven, 0 ]
  ifnot
  [ nAuxSides, auxIncr, nAuxBreaks, incr = nDoubles + firstPageEven, firstPageEven, 0, lastPageOdd ]
let totAuxSides = nAuxSides * nCopies + nAuxBreaks
page = 0
test afterPage ls totAuxSides ifso		// then need to fill Aux tray
	[
	nextHandling = pTSFirst
	copy = afterPage / nAuxSides
	test copy eq nCopies then page =  bkPage  or
	  [
	  nextCopy = printerForward? nCopies-copy, copy+1
	  relImageIndex = (afterPage rem nAuxSides)*2 + 1 - auxIncr
	  if printerForward do relImageIndex = nPages -1- relImageIndex
	  page = relImageIndex * (size PageG/16) + pDoc>>DocG.Pages
	  ]
	]
  ifnot	// then put paper out - from aux tray or as single sheets from main tray
	[
	afterPage = afterPage - totAuxSides
	nextHandling = pTSSecond
	test afterPage < nAuxBreaks then	page = 1 or		// no image -- just feed breakpage thru
	  [
	  afterPage = afterPage - nAuxBreaks
	  test afterPage < nSheets*nCopies ifso		// if there are more data pages . . .
		  [
		  copy = afterPage/nSheets
		  nextCopy = printerForward? copy+1, nCopies - copy
		  relImageIndex = afterPage rem nSheets
		  test (relImageIndex +auxIncr) eq nSheets then page = 1	// no image -- just feed page thru
		  or		// there is an image to do
		    [
		    if relImageIndex < incr do nextHandling  = pSSMain // single sheet
		    let pageIndex = (afterPage rem (nSheets - auxIncr))*2 + 1 - incr
		    unless printerForward do pageIndex = nPages - 1 - pageIndex
		    page = pageIndex*(size PageG/16) + pDoc>>DocG.Pages
		    ]
		  ]
	    ifnot		// no more data pages
		  [ if afterPage <nSheets*nCopies + nBreaks do  [ nextHandling = pSSMain; page = bkPage ] ]
	] ]
if page eq bkPage then nextCopy = breakPageCopy
resultis page ]
// -----------------------------------------------------------------------------------
// SetupRecovery(pDoc, lvFailureCode, lvXportModel, restartPage, code; numargs na)
// -----------------------------------------------------------------------------------
//This is truly serious - Spruce aborts the file and restarts from the beginning.
//	$$5070 070: starting over - Aux tray empty too soon
// Aux tray should have had a sheet with a first side - got lost somewhere
// $$5071 071: Aux tray empty malfunction 
// May be spurious
//	$$5072 072: Aux tray has trash in it - please empty it 

//These represent external problems - Spruce needs user intervention to recover nicely
//if the Aux tray is involved.
//	$$5080 080: Aux tray misfeed
//	$$5081 081: Processor interlock open
//	$$5082 082: Zone IIA jam
//	$$5083 083: Zone IIB jam
//	$$5084 084: Zone III jam
//	$$5085 085: Jam - Remove 1 sheet from paper path/aux tray.
//	$$5086 086: Jam - Remove 2 sheets from paper path/aux tray.
//	$$5087 087: Jam - Remove 3 sheets from paper path/aux tray.

//Even if these were detected while printing, assume Penguin did not start feeding paper
//(last page must be reprinted, but nothing in the paper transport mechanism was damaged)
//	$$5090 090: Main tray misfeed

//these assume last page was printed successfully if detected while printing first side
//If we are wrong, error 70 will occur
//	$$5100 100: Printer is in local mode. Remove Auditron key.
//	$$5101 101: ROS malfunction - Page sync never went off.
//	$$5102 102: ROS or printing malfunction - Orbit timed out
//	 -- page sync never arrived, or engine died, or Orbit got behind.
//	$$5103 103: ROS or printing malfunction - Alto program timed out
//	 waiting for Orbit microcode; page sync never arrived, or engine died, or Orbit got behind.
//	$$5104 104: Not ready - controller is in wait state.
//	$$5105 105: Sorter interlock open
//	$$5106 106: bad 6800 subsequence code
//	$$5107 107: bad 6800 main sequence code
//	$$5108 108: Ink low
//	$$5109 109: Trays don't match
//	$$5110 110: Fuser not ready
//	$$5111 111: Main tray misfeed
//	$$5112 112: Cycle control state error in 6800
//	$$5113 113: 6800 reports abnormal condition, but did not specify what it is
//	$$5114 114 Unexpectedly lost Printing state or didn't reach expected Cycledown state
//(	Penguin Print [printloop])
//	$$5115 115: Main paper tray empty
//	$$5116 116: Penguin not communicating - please check it(ROSCheck)
//	$$5117 117: Penguin returned unknown malfunction code
//	$$5118 118: Laser appears to be off
//	$$5119 119: Something is wrong, but don't know what

//These assume the last page was imaged correctly
//	$$5120 120: Timeout waiting for Standby - ready Penguin, please.
//	$$5121 121 Penguin not communicating - please check it
//	 (Print startup)
//	$$5122 122: Penguin in standby, but won't accept print command
//  $$5123 123: Printer in power-on mode:  Push StartPrint button
//	$$5124 124: Paper exit jam:  Please clear
//	$$5125 125: Output bin-entry jam: Please clear

//these are precautionary stops - spruce proceeds after user intervention
//	$$5130 130: Output bin full: empty it then type P
//  $$5132 132: Main tray empty - add paper

//these don't stop printing
//	$$5300 300: Some pages too complicated, but all were printed
//	Orbit got behind ROS, but page terminated normally.
//	$$5301 301: Some pages too complicated, but printing continued
//	Orbit got behind ROS, quit early when SendVideo went away.
//	$$5310 310: Internal problem; please inform Spruce mainenance personnel
//	Left over table is not big enough.
// -----------------------------------------------------------------------------------
// SetupRecovery(pDoc, lvFailureCode, lvXportModel, restartPage, code; numargs na)
// -----------------------------------------------------------------------------------

and let SetupRecovery(pDoc, lvFailureCode, lvXportModel, restartPage, code; numargs na ) = valof
[
test na eq 5 then [ @lvFailureCode = code ] or [ code = @lvFailureCode ]
unless code resultis restartPage
if code < 75 resultis 0 //paper in aux tray didn't come out even with 2nd side images
let backup = 0
if  code < 85 then
	[
	let xpm =@lvXportModel
	while xpm ne 0 do [ backup = backup + (xpm & 1); xpm  = xpm rshift 1 ]
	if backup do 
		[ stopsPrinting = (backup lshift 8) + jam; if twosided do @lvFailureCode = backup + 84 ]
	]
backup = backup + nPagesPrinted - restartPage
let  nFirstSides, nSecondSides, nSingleSheets =  0, 0, 0
if (nPagesPrinted ge backup) & (restartPage ge (nPagesPrinted - backup)) do  //for each destroyed sheet
		[
		let x = NextSheet(pDoc, restartPage-1)
		test nextHandling eq pTSFirst then nFirstSides = nFirstSides + 1
		or test nextHandling eq pTSSecond then nSecondSides = nSecondSides + 1
		or nSingleSheets = nSingleSheets + 1
		if restartPage eq (nPagesPrinted - backup) then break
		restartPage = restartPage - 1
		] repeat
test BinCounters!AuxRecoveryCount then BinCounters!AuxRecoveryCount = BinCounters!AuxRecoveryCount+ backup or
	BinCounters!AuxRecoveryCount = Max(nSecondSides - nFirstSides, 0)
resultis restartPage
]
// -----------------------------------------
// InitializeHardware, ROSCommand, EngineCommErr, SelectBin.
// -----------------------------------------

and InitializeHardware() be
[	if Debug return
	InitRam(0)			//Reset all RAM tasks, restart Trident if necessary
	DoFunc(fControl, 1)		//Reset Orbit
]

and ROSCommand(command) = valof
	[
	rosCount = (command eq pResetCount)? 0, rosCount + 1
	DoFunc(fROSCommand, command)
	if Debug resultis 0
	let tim = @RTC
	until (@RTC - tim) ge eTicks do 
		[ 
		ros12 = ROSStatus(12)
		if (rosCount & #377) eq (ros12 & #377) then break 
		 ]
	resultis EngineCommErr(ros12)
	]

and let EngineCommErr(x) = valof

// returns 
//	1 - if count kept by penguin does not agree with rosCount
//	2 - if a command was rejected because penguin didn't recognize it
//	3 - if penguin didn't accept a command because its buffers were full (of other commands)
	[
	if (x & #377) ne (rosCount & #377)   resultis 1
	if (x & #10000) eq #10000   resultis 2+ (x&#2000 eq 0?0,1)
	resultis 0
	]

and SelectBin(bin) = valof
[
let tray = 0
if ((Capabilities & mMailbox) ne 0) & (BinCounters!bin le 50) & (BinCounters!0 le 50) then tray = bin
if ((Capabilities & mMultiBin) ne 0) then
	[
	test (BinCounters!bin < 50) then tray = bin
	or tray = Min(bin+1, 18)
	]
resultis tray
]

// December 15, 1978  10:04 AM first cut - derived from puffinprint, sort of.
// January 2, 1979  4:40 PM more work on ROSCheck
// January 4, 1979  3:12 PM first cut at xportModel, more work on ROSCheck
// January 15, 1979  3:53 PM fix ROSCommand
// January 19, 1979  3:04 PM cleanup and beautification
// January 30, 1979  11:08 AM change order of tests in EngineCommErr
// February 14, 1979  9:47 AM turn on led control -- release 2
// February 28, 1979  3:19 PM increase printHysteresis to 30
// March 1, 1979  12:26 PM be tougher on unexpected printing state from 6800
// April 10, 1979  12:45 PM first cut - twosided printing
// April 17, 1979  10:01 PM cleanup
// May 22, 1979  11:24 AM upgrade to os16
//  May 23, 1979  2:19 PM, fix measure interface
// June 28, 1979  4:40 PM, allow for spurious 'auxtray empty' msg and primitive error recovery
// August 6, 1979  10:49 AM, use SetupPrint in SprucePrint; handle simplex, overflow protection, output modes
// August 10, 1979  4:14 PM, first cut new error handling
// August 14, 1979  5:06 PM, more cleanup work
// August 21, 1979  10:20 AM, put out break page if changing bins, cleanup
// August 29, 1979  2:03 PM, multibin doesn't use same overflow bin as mailbox
// September 13, 1979  9:48 AM, force user interaction for jam on two sided print
// September 13, 1979  4:45 PM, fix handling of jam on 4th sheet
// September 20, 1979  9:55 AM, work on error messages
// September 28, 1979  9:58 AM, if file is just a break page, treat as single side
// October 3, 1979  4:24 PM, use new PageG structure
// October 17, 1979  3:19 PM, enable bin-positioning commands
// October 26, 1979  12:16 PM, put 'MORE IN OVERFLOW BIN' breakpage in mailbox
// October 26, 1979  3:59 PM, increase timeout to 11 seconds to allow bin movement to complete
// January 15, 1980  10:12 PM, remove kludge for spurious aux tray empty msg on switch to 2nd side
//		"	add errors 124 & 125 for exit jams
// February 27, 1980, 4:27 PM, try to get intermediate break pages in right bin
// March 3, 1980 12:06 PM, try some more
// July 16, 1980, 4:38 PM, if there is a break page breakPage value is 1 (instead of true)
// July 23, 1980, 4:07 PM,  cycledown gracefully if bin full 
// February 2, 1981  4:21 PM,  don't swat if bad band entry probably due to leftover overflow
// February 5, 1981  10:49 AM,  remove  bin choosing code, put into sprintUtils
// February 5, 1981  4:57 PM,  add first cut at BinSerials code