// P R I N T   
// errors 1000
//

get "Spruce.d"
get "SpruceFiles.d"
get "Orbit.d"

// outgoing procedures
external
	[
	DrivePrinter
	]


// incoming procedures
external
	[
	SpruceError
	Max
	Min
	InitRam
//SprucePrint
	AddToBin
	Announce
	AwaitPageSync
	PrintNext
	ReadBands
	ReadClock
	ReadFont
//CURSOR
	CursorChar
	CursorDigit
// SprucePrintRes
	DoFunc
	ROSStatus
// Timer (debug only)
	SetTimer
	TimerHasExpired
//OS
	Zero
	DisableInterrupts
	EnableInterrupts
	]

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


// internal statics
static
	[
	rosCount = 0 // Pimlico/puffin command count
	rosSyncError = false // Pimlico/puffin -- sync problem
	startRun = true
	// delay ~~~~ debugging: delay per page to simulate printing
	debDelay = 0 // delay no delay std. 
	// delay ~~~~
	]

// File-wide structure and manifest declarations.

manifest [
	eTicks = 4	
	timeOut=15*27
	printHysteresis = 15 // print this many before allowing suspension, each time in
	  // ~~ above number may want to increase for large runs, or something?
	RTC=#430
	debugTrailSize = 10*3

	// Failure Code ranges -- see ROSCheck
	maxNotReady = 9 // not ready, but probably will be soon
	maxManual = 19 // Manual intervention required
	maxJam = 99	// Error during operation
	minDocumentError = 300 // Document inherently hard to print
		]



// -----------------------------------------------------------
// 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
	let CommTab=vec 3
	CommTab=(CommTab+1)&(-2)
	CommTab!0=-1

	InitializeHardware(true)
	InitMeasure(measureTable, 2000, measureVec)
	let knockKnock = false		// time to let somebody in if true
	let needInit, needFonts = true, true
	let copy, nfRecs, LeftOver, LeftOverGuard, FontTable, buf = nil, nil, nil, nil, nil, nil
	let np = nil
	let nextPage = PrintNext(pDoc, lv copy, nPagesPrinted)

//--------------------------------
// Printloop
//----------------------------------

[Printloop
	let page=nextPage     // Find out which page to print next:
	if page eq 0 then [ knockKnock = false; break ]
	if knockKnock then break // go let somebody in
	if stopsPrinting eq binFull then [ @lvFailureCode = 130; break ]

	if needFonts do
	[				// Read in font and relocate the pointers to characters:
	let font=page>>PageG.fontLoad*(size FontG/16)+pDoc>>DocG.Fonts
		// Next call returns location of ICC table,
		FontTable = ReadFont(font, LowAdr, nRecs)
		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

		let nRecords= xmFonts? (pDoc>>DocG.ICCtotal+bandRecordSize-1) rshift logBandRecordSize,
				font>>FontG.nRecords
	nfRecs=(nRecs-nRecords)
	LeftOver=LowAdr+(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
	]

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

if needInit do
	[
	InitializeHardware(false)			// Initialize the Orbit, etc.
	let tim = @RTC
   unless Debug do  // wait for ready or some serious condition or for long enough
	[
	np = ROSCheck(lvFailureCode, nPagesAlreadyPrinted, false)
	if np eq -1 break // ready
	if @lvFailureCode > maxNotReady % (@RTC-tim) > timeOut resultis np
	] repeat

// 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)

	DoFunc(fControl, 21b)
// Turn off adCommandBeamOn, if on, revert to normal modulation--
// ~~ causes glitch	AdCommand(AdapterScales)
	DoFunc(fROSCommand, adBufferReset)
		ROSCommand(#63346)	// Start, using std. 3 color video queue
		AwaitPageSync(0)
	needInit = false
	]

		let nC = nil
		nextPage = PrintNext(pDoc, lv nC, nPagesPrinted+1)
		unless nextPage do ROSCommand(#63030) // Stop Print (at end of copy)

// 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
	let tim=@RTC
// Start the microcode!!!!
	DoFunc(fSlot, CommTab, 20000, nBands-1, FAvalue, LeftOver,
			FontTable, buf-1, nil, nil, (copy lshift 5)+4)

if Debug then // ~~delay
	[ // ~~delay
	while (@RTC-tim) < debDelay loop	// ~~delay
	tim = @RTC // ~~delay don't invoke timeout code below
	] // ~~delay

// Assume the page we are printing will be OK:
	nPagesPrinted=nPagesPrinted+1
	CursorDigit(nPagesPrinted)
	copy = nC
// See who's (maybe) knocking at my door
// This logic is copied from SpruceUtilsRes: 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

	if (nextPage>>PageG.fontLoad ne page>>PageG.fontLoad) do needFonts = true

// Now see if we actually printed this page properly.  First, wait for Orbit to announce it is finished.
	let imageTimeout =  10*27
	while @#720 ne 0 do if (@RTC-tim) gr imageTimeout then
		[
		Blast()		// clear hardware, #720
		 // Machine dead or Orbit got behind and it wasn't detected
		 // Will generally be overriden by more specific analysis
		@lvFailureCode = 19 // 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 = 18 ]
	if LeftOverGuard!1 ne -1 then
	  [ Announce(18); nonF = 310 ]
	if nonF then @lvFailureCode = nonF
// Now try for more specific engine status check

	np=ROSCheck(lvFailureCode, nPagesAlreadyPrinted, true)

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

//Now decide what to do:
	let malfunction = @lvFailureCode
	if malfunction ge minDocumentError then malfunction = 0 // will not stop

// Now react to the analysis: if at this point nothing is wrong and sheets are feeding, steam ahead. 

	unless ((nPagesPrinted rem 3) ne 2) % malfunction  do [ AddToBin(50); loop ]

		InitializeHardware(true)
		// if malfunction detected outside of ROSCheck, set np
 
		let backup = 1  //wrong?
		if np eq -1 then np = nPagesPrinted-backup
		// account for previous increment
		nPagesPrinted = Max(np-1, nPagesAlreadyPrinted)
		break
]Printloop repeat

	InitializeHardware(true)
	CloseMeasure()
	resultis (knockKnock % @lvFailureCode)? nPagesPrinted, -1
]

// --------------------------------------------------------------------------
// nPagesPrinted = ROSCheck(lvFailureCode, nPagesAlreadyPrinted, running, 
//		lvXportModel)     added for puffin
// --------------------------------------------------------------------------

// analyze ROS status
// nPagesPrinted = -1 if there are no malfunctions, ROS is ready to print
//			otherwise, the number of pages to report as having been printed
// @lvFailureCode will contain an analysis of the current state. The code values are
//			described below.
// nPagesAlreadyPrinted places a lower bound on the nPagesPrinted result. This 
//			prevents loops through this code from backing up too far.
// running		true if caller thinks main drive is allowed to be on for "ready"
//			result, false otherwise (waiting for ready).

// 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 and a new page value -- the last page certain to have been reliably
// printed. One possible condition is "no malfunction detected", which will only
// be issued for printers that give no positive indication when they are merely
// busy doing normal things.
// N.B. @lvFailureCode is not changed when no conditions are found

// The failure codes divide into ranges:
// 0-9		Not ready, but no indication of problems or will clear up automatically
//		(includes fuser cold, warmup mode, clearing revolutions, etc.)
// 10-19	Manual intervention required before machine can become ready
//		(paper low, power not there, etc.)
// 20-99	Jams and other operational malfunctions (many codes provided so that
//		each device can report machine-dependent jam explanations)
// 100-200	Inherent problems (e.g., insufficient bandwidth somewhere) that are
//		not curable by mere mortals. Attempts to improve the situation should be
//		abandoned.
// 200-300	Inherent problems encountered, but they did not terminate printing. One
//		should expect that one or more pages were improperly imaged.

//  right byte is status code returned from machine,
//	left byte is status code returned from ROSCheck

// A check, designed chiefly for Dover, 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

// ROSCheck (cont)

and ROSCheck(lvFailureCode, nPagesAlreadyPrinted, running, lvXportModel) = valof
[

let ros8 = ROSStatus(8)
let ros9 = ROSStatus(9)
let localMode, scanning = nil, true
let seqStatus, malFnStatus = nil, nil

seqStatus, malFnStatus, seqStatus = ROSStatus(13), seqStatus&#377, seqStatus rshift 8

let ready = 
	ROSStatus(12) ge 0 & malFnStatus eq 0 // &
	  // (seqStatus eq 3 % // 3=standby, 4=printing, 9=clearing
	    // (running & (seqStatus eq 4 % seqStatus eq 9)))
if ready then
	[
	localMode = (ROSStatus(0)&#20000) ne 0
	unless running do
	  [
	  let lineCount = ROSStatus(7) & #170000
	  let rtc = ReadClock()
	  until (ReadClock()-rtc) ge scanTicks loop
	  scanning = (ROSStatus(7)&#170000) ne lineCount
	  ]
	if localMode % not scanning then ready = false
	]

if ready % Debug resultis -1

// Analyze tables to identify the problem or condition preventing ready

let tab = table [ // error code,, machine code
	10 lshift 8 + #32 // 1A 2/2 low paper.
	52 lshift 8 + #00 // 00 X missed.
	53 lshift 8 + #04 // 04 malfunction memory set.
	54 lshift 8 + #10 // 08 1/1 FIRE!!!!!!!!
	55 lshift 8 + #16 // 0E 1/4 Transport A.
	56 lshift 8 + #34 // 1C 2/3 Paper grip.
	57 lshift 8 + #36 // 1E 2/4 Transport B.
	58 lshift 8 + #50 // 28 3/1 Fuser interlock open.
	59 lshift 8 + #54 // 2C 3/3 Bottom tray interlock open.
	60 lshift 8 + #56 // 2E 3/4 Transport C jam.
	61 lshift 8 + #74 // 3C 4/3 Front door open.
	62 lshift 8 + #116// 4E 5/4 Toner call.
	63 lshift 8 + #150// 68 7/1 Functional interlock open.
	0  ]
// Codes generated elsewhere:
//	7 -- no specific malfunction detected, but something is wrong
//	16 -- page sync never turned off
//	17 -- several consecutive unidentified errors
//	18 -- Orbit timed out -- page sync never arrived, or engine died or got behind
//	19 -- Alto code timed out -- same symptoms
//	300 -- Orbit got behind, but page terminated normally
//	301 -- Orbit got behind, had to quit early
//	310 -- Left over table got too large. Please report to fixer.
// ROSCheck (cont)

// Condition interpreter

let code = 0

		test rosSyncError then code = 51
		 or test ROSStatus(12) < 0 then code = 50
		 or test malFnStatus eq 0 then code = selecton seqStatus into
			[ case 1: case 2: 2; case 9: case 10: 3; case 4: 8; case 5 to 8: 9; default: 0 ]
		 or test #200 le malFnStatus & malFnStatus le #240 then code = 64
                 // 80-A0 T events missed
		 or while @tab do
			[
			let entry = @tab
			if malFnStatus eq (entry&#377) then [ code = entry rshift 8; break ]
			tab = tab + 1
			]

// No other explicit problems, report local mode, or laser off, or unknown problem
unless code do code = localMode? 11, scanning eq 0? 13, 7
@lvFailureCode = code
 let backup = code le maxManual? 0, 1
resultis Max(nPagesAlreadyPrinted, nPagesPrinted-backup)
]
// -----------------------------------------
// Hardware setup for various devices.
// -----------------------------------------

and InitializeHardware(stop) be
[	if Debug return
	InitRam(0)			//Reset all RAM tasks, restart Trident if necessary

	DoFunc(fControl, 1)		//Reset Orbit

	rosCount, rosSyncError = 0, false
	ROSCommand(#60000, false)		// Reset command count
	ROSCommand(#63050)			// Read out malfunction data, clear malf.
	// ROSCommand(#63217)			// SetPageSyncEvent to 25
	if stop then ROSCommand(#63040)	// Stop now.
]

and Blast() be InitializeHardware(false)

and ROSCommand(x, sync; numargs na) be

	[
	if na<2 then sync = 4
	let good = true
	for i = 1 to sync? sync, 1 do
	  [
	  DoFunc(fROSCommand, x)
	  unless sync break
	  let tim = @RTC; good = false
	  while (@RTC-tim) le 25 do if (rosCount&#377) eq (ROSStatus(12)&#377) then
		[ good = true; break ]
	  if good break
	  ROSCommand(#60000, false)
	  rosCount, rosSyncError = 0, false
	  ]
	unless good do rosSyncError = true
	rosCount = rosCount+1
	]
// February 16, 1979  10:12 AM derived from Spruceprint v8
// April 27, 1979  3:51 PM put in spruce 10 stuff - still needs work
// May 17, 1979  4:42 PM repair errors
// May 23, 1979  2:41 PM, fix interface to measure stuff
// February 2, 1981  4:27 PM,  don't swat if bad band entry probably due to leftover overflow