// PrintOrbit.bcpl

// errors 3400,3500

get "PDInternals.d"
get "Orbit.d"

// outgoing procedures
external
	[
	POrbit
	POrbitInit
	]

// incoming procedures
external
	[
//PrintOrbitHdwFns
	InitializeHardware
	DoFunc
	ROSCommand
	AdCommand
	SetupAdapter
	AddQEntry
	FeedASheet
	AwaitPageSync
	ROSCheck

//PRINT
	PrintError

//PDPRINT
	Min
	PDError

//CURSOR
	CursorChar
	CursorDigit
	CursorToggle

//OS
	Zero
	MoveBlock
	StartIO

//PrintDisk
	PrintDiskInit
	PrintDiskRead
	PrintDiskCheck
	PrintDiskStop
	]

// incoming statics
external
	[
	nPrinterColors
	printerDevice
	useStandardQueue
	SLOTTimeOut
	Debug

//PRINTORBITINIT
	nVisibleBands
	nLeadingBands
	nTrailingBands
	FA
	orbitSignalBand

//PRINTORBITHDWFNS
	Func
	]

//outgoing statics
external [ debugTrail ]
static [ debugTrail ]

// internal statics
static
	[
	coldStart
	orbitStat
	readyTimeOut=3*27
	]

// File-wide structure and manifest declarations.

manifest [
	OrbitBandWidth=16
	RTC=#430
	loSize=30	//Enough for one character, end, slop
	debugTrailSize=40*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 = 150 // Document inherently hard to print
	]

manifest
 [	//P commands
	PResetRemoteQueue=#63010
	PStartRemoteQueue=#63006
	PStart3ColorVideo=#63346
	PStart3ColorScan=#63246
	PStart4ColorVideo=#63306
	PStart4ColorScan=#63206
	PAppendQueueEntry=#63020
	PSetVideo=#63204
	PNoPaperAction=#63003
	PFeedPaper=#63203
	PReleasePaper=#63103
	PSinglePass=#63303
	PNoColor=#63001
	PSetColor6=#63201	//usually black
	PSetColor7=#63101	//usually cyan
	PSetColor9=#63041	//usually magenta
	PSetColor11=#63021	//usually yellow
 ]

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

let POrbitInit(pg, nPages, nCopies; numargs na) be
[
	coldStart=true
	let debugTrailVec=vec debugTrailSize
	compileif DebugSw then
	 [ debugTrail=debugTrailVec; debugTrail!0=1; debugTrail!1=debugTrailSize-4 ]

	[
	SetupAdapter()
	InitializeHardware(true)
	let FailureCode=ROSCheck(false)
	if FailureCode eq 0 then break
	PrintError(3400+FailureCode)
	] repeat

	if na eq 0 then return

// If using a standard queue, check to be sure it's consistent
	if useStandardQueue then
		[
		if nPages rem nPrinterColors ne 0 then PDError(3589)
		for i=0 to nPages-1 do
			[
			let toner=pg>>PageG.ColorPass
			let expToner=selecton (i rem nPrinterColors) into
				[ case 0: 2; case 1: 3; case 2: 1; case 3: 0 ]
			if toner ne expToner then PDError(3588)
			let f,s=0,0
			if (i rem nPrinterColors) eq 0 then f=1
			if (i rem nPrinterColors) eq (nPrinterColors-1) then s=1
			if pg>>PageG.strip ne s % pg>>PageG.feed ne f then PDError(3587)
			pg=pg+lPageG
			]
		return
		]

// Do P machine queue creation (Pimlico, Puffin)
// establish queue entries: first, a null entry, and then the real entries
	ROSCommand(PResetRemoteQueue)
	ROSCommand(PAppendQueueEntry)	//null rev
	if nCopies*nPages gr 40 then PDError(3590)	//Too many entries
	for i=1 to nCopies do
	 [	let p=pg
		for j=1 to nPages do	
		 [	let paperAction=PNoPaperAction
			if p>>PageG.feed then paperAction=PFeedPaper
			if p>>PageG.strip then
				[
				test p>>PageG.feed then paperAction=PSinglePass
				  or paperAction=PReleasePaper
				]
			if (nPrinterColors eq 3)&(p>>PageG.ColorPass eq 0) then
				PDError(3591)
			let tonerColor=selecton p>>PageG.ColorPass into
				[ case 0:	PSetColor6;	//Black
				  case 1: PSetColor7;	//Cyan
				  case 2: PSetColor9;	//Magenta
				  case 3: PSetColor11 ]	//Yellow
			AddQEntry(tonerColor, paperAction)
			p=p+lPageG
		 ] //for all pages
	 ] //for all copies
	ROSCommand(PStartRemoteQueue)	//Start printer turning
	coldStart=false
]

// POrbit -- main routine to print a single page with the Orbit

and POrbit(pg, lowAdr, highAdr, last) = valof
[
	let FailureCode=0

	let debugTrailVec=vec debugTrailSize
	compileif DebugSw then
	 [ debugTrail=debugTrailVec; debugTrail!0=1; debugTrail!1=debugTrailSize-4 ]

	let nBands=nLeadingBands+nVisibleBands+nTrailingBands
	let nImageBands=pg>>PageG.LastBand-pg>>PageG.FirstBand+1

	lowAdr=(lowAdr+1)&(-2)		//Make it even
	let BandCommands=lowAdr
	lowAdr=lowAdr+(nBands+nImageBands)*2

	let FontTable=lowAdr		//it's even
	lowAdr=lowAdr+nImageBands	//never need more than nImageBands chars

	let LeftOver=vec loSize+1
	LeftOver=(LeftOver+1)&(-2)
	let LeftOverGuard=LeftOver+(loSize&(-4))-4
	LeftOver!0=0			//Initialize LeftOver table
	LeftOverGuard!1=-1

	let FirstBuffer=nil
	let nBuffers=PrintDiskInit(pg, lowAdr, highAdr, lv FirstBuffer)

//Now build font table: char i points to buffer i
	let p=FirstBuffer
	for i=0 to Min(nImageBands,nBuffers)-1 do
		[
		FontTable!i=p
		p=p!-1
		]

//Now build band command list
	let nextCharCode=0
	let nextImageS=pg>>PageG.FirstBand*pg>>PageG.BandWidth
	let p=BandCommands
	for i=0 to nBands-1 do
		[
		while nImageBands ne 0 &
		  nextImageS ls (i-nLeadingBands+1)*OrbitBandWidth do
			[
			nImageBands=nImageBands-1
			p!0=#100000+nextCharCode
			nextCharCode=(nextCharCode+1) rem nBuffers
			p!1=((nextImageS&(OrbitBandWidth-1)) lshift 12)+
				pg>>PageG.BitMargin+FA*16
			nextImageS=nextImageS+pg>>PageG.BandWidth
			p=p+2
			]
		p!0=0; p!1=0; p=p+2
		]

//Read first nBuffers worth of information into disk buffers
	let nR,nC=nil,nil
	for i=1 to nBuffers do nR=PrintDiskRead()
	nC=PrintDiskCheck() repeatuntil nC eq nR

// Initialize the Orbit, printing engine, etc.
	InitializeHardware(false)

// Now wait for the printer to indicate ready
	let tim = @RTC
		[
		FailureCode=ROSCheck(not coldStart)
		if FailureCode eq 0 then break // ready
		if FailureCode > maxNotReady % (@RTC-tim) > readyTimeOut then
			[ InitializeHardware(true); resultis FailureCode ]
		] repeat

	DoFunc(fControl, 21b)
	AdCommand(adBufferReset)

// Feed a sheet (on those printers that require it)
// If Dover is already running, this StartPrint may not do the right
// thing, because it may be sent during the "dead" time near CS-5
	FeedASheet()
	unless AwaitPageSync(0) do PrintError(3592)
// Cold start for Dover requires an idle cycle
	if printerDevice eq printerDover & coldStart ne 0 then
		[ AwaitPageSync(1); FeedASheet(); AwaitPageSync(0) ]
	coldStart=false

// Calculate a ROS command table for the device:
	let CommTabNoFeed=vec 3
	CommTabNoFeed=(CommTabNoFeed+1)&(-2)
	CommTabNoFeed!0=-1
	let CommTabFeed=vec 10
	CommTabFeed=(CommTabFeed+1)&(-2)

	let CommTab=nil
	switchon printerDevice into
	    [
	    case printerDover:
		CommTab=CommTabFeed
		CommTab!0=orbitSignalBand+2
		CommTab!1=adExternalCommand1+(last? 0,1)
		CommTab!2=orbitSignalBand+1
		CommTab!3=adExternalCommand1
		CommTab!4=10000b+orbitSignalBand
		CommTab!5=32*256		//Status bits 0-3 of word 8
		CommTab!6=-1
		endcase
	    case printerSequoia:
		[
		CommTab!0=orbitSignalBand
		let stopPrint=#60005
		if printerDevice eq printerSequoia then stopPrint=#60000
		CommTab!1=last?stopPrint,#60001 //Stop unless proven otherwise (below)
		CommTab!2=-1
		]
		endcase
	   case printerPuffin:
	   case printerPimlico:
		[
		CommTab = CommTabNoFeed
		if last ne 0 & useStandardQueue ne 0
			do ROSCommand(#63030) // Stop Print (at end of copy)
		endcase
		]
	    ]

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

// Start the microcode!!!!
	tim=@RTC
OrbitStart:
	DoFunc(fSlot, CommTab, 20000, nBands-1, FA lshift 8, LeftOver,
			FontTable+#100000, BandCommands-1)

// imageS is coordinate of first scan-line of the NEXT image band
	let imageS=(pg>>PageG.FirstBand+1)*pg>>PageG.BandWidth

// Loop waiting for ORbit to finish.
	let imageTimeout = SLOTTimeOut*27
	while @#720 ne 0 do 
	[
	if (@RTC-tim) gr imageTimeout then
		[
		InitializeHardware(true)		// clear hardware, stop printer
// Machine dead or Orbit got behind and it wasn't detected
// Will generally be overriden by more specific analysis
		FailureCode = 99 // Manual intervention required
		break
		]

// orbitS is coordinate of first scan-line in the band being composed
// in a band buffer by Orbit microcode
	let orbitS=(nBands-nLeadingBands-1-Func!3)*OrbitBandWidth
// If first scan-line in Orbit's current band is beyond first scan-line in
// the NEXT disk buffer, the CURRENT disk buffer can be reused
	if orbitS gr imageS then
		[
		let onR=nR
		nR=PrintDiskRead()
		if onR ne nR then imageS=imageS+pg>>PageG.BandWidth
		]
	nC=PrintDiskCheck()
	if nC-nR ge nBuffers-1 then FailureCode=100	//disk behind
	]

// Check Orbit status
	orbitStat=Func!9		//Orbit printing status.
	if orbitStat<<STATUS.invalidBandEntry then FailureCode=98
	test orbitStat<<STATUS.prematurePageAbort then [ FailureCode=102 ]
	  or if orbitStat<<STATUS.behind then [ FailureCode=101 ]
	if orbitStat<<STATUS.timeout then [ FailureCode=18 ]
	if LeftOverGuard!1 ne -1 then [ FailureCode=103 ]

// Now try for more specific engine status check
	if FailureCode eq 0 then FailureCode=ROSCheck(true)

// Check COUNT-H signal, paper did not arrive -- else re-print page (Dover only).
	if printerDevice eq printerDover &
		((CommTab!5)&1) eq 0 then FailureCode=97

	resultis FailureCode
]

//and Announce(n) be
//[
//	CursorChar((n ge 16? $K,$P))
//	for i=0 to 3 do
//		[
//		if (n&10b) ne 0 then CursorToggle(i)
//		n=n lshift 1
//		]
//]