// P R I N T O R B I T H D W F N S   

// errors 3400,3500

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

// outgoing procedures
external
	[
	InitializeHardware
	DoFunc
	ROSCommand
	AdCommand
	SetupAdapter
	AddQEntry
	FeedASheet
	AwaitPageSync
	ROSCheck
	]

// outgoing statics
external
	[
	Func
	]
static
	[
	Func
	]

// incoming procedures
external
	[
	MoveBlock
	StartIO
	]

// incoming statics
external
	[
	printerDevice
	useStandardQueue
	nPrinterColors
	Debug

//PRINT ORBIT
	debugTrail

//PRINTORBITINIT
	BitScale
	MotorScale
	BitClock
	MotorSpeed
	LineSyncDelay
	PageSyncDelay
	VideoGate
	]

//internal statics
static
	[
	rosCount = 0	//P,Pimlico command count
	rosSyncError = false // Pimlico -- sync problem
	jogT1 = 10*27
	jogT2 = 6*27
	]

// File-wide structure and manifest declarations.

manifest
	[
	RTC=#430
	]

// ROSCheck values
structure CT:
 	[
 	invert		bit 1
 	failureCode	bit 7
 	wordIndex	bit 4
 	bitIndex	bit 4
 	]

manifest // factors for creating table entries
	[
	Invert		= #100000
	Code		= 256
	Word		= 16
	Bit		= 1
	]

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
//P printSeq states (these are simply pseudo malFunction codes)
	Pwarmup=#301
	Pstandby=#302
	Pprinting=#303
	Pendofrun=#304
	Plowpaper=#305
	Pbadfeed=#306
	
Pbadstrip=#307
	Pclearing=#310
	PmalfClear=#311
	Pillegal1=#312
	Pillegal2=#313
 ]


// -----------------------------------------
// Low-level Orbit functions.
// -----------------------------------------

let InitializeHardware(stop) be
[
	if Debug return
	DoFunc(fControl, 1)		//Reset Orbit

	switchon printerDevice into
	[
case printerDover: ROSCommand(adExternalCommand1); endcase

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

and DoFunc(function, arg1, arg2, arg3, arg4, arg5, nil,nil,nil,nil,nil) = valof
[
	compileif DebugSw then [ DebugEnter(function, arg1) ]
	if Debug then resultis 0
	if Func eq 0 then
		[
		Func= table [ 0;0;0;0;0;0;0;0;0;0;0;0;0;0 ]
		Func=(Func+1)&(-2)
		]
	MoveBlock(Func, lv function, 11)
	if function eq fStatus then Func!0=fDBCWIDIn
//	if refreshIdle then @Func=(@Func)%#100000
// Give pointer to microcode stuff.
	@#720=Func
// Wake up microcode
	StartIO(#4)
// Wait for microcode to complete
	if function ne fSlot then while @#720 ne 0 do loop
	if function eq fStatus then resultis Func!9
	resultis Func!8
]

and DebugEnter(a, b) be
[	compileif DebugSw then
		[
		let m=debugTrail!0
		let dm=debugTrail+m
		dm!1=a; dm!2=b; dm!3=@RTC		//Time
		m=m+3; if m ugr debugTrail!1 then m=1
		debugTrail!0=m
		]
]

and ROSStatus(w) = DoFunc(fROSStatus, w*(4*256))

and ROSCommand(x, sync; numargs na) be
switchon printerDevice into
[
   case printerPuffin: na=2;sync=1;//endcase intentionally omitted
   case printerPimlico:
	[
	if na<2 then sync = 4
	test x eq #60000 then rosCount=0
	or if printerDevice eq printerPuffin then rosCount=(rosCount+1) rem 256
	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 eq (ROSStatus(12)&#377) then
			 [ good = true; break ]
		if good then break
		ROSCommand(#60000, false)
		rosSyncError = false
	 ]
	unless good do rosSyncError = true
	unless printerDevice eq printerPuffin do rosCount = (rosCount+1) rem 256
	endcase
	]
   default: DoFunc(fROSCommand, x)
]

and AdCommand(x) be DoFunc(fROSCommand, x)


// -----------------------------------------
// Initialize adapter.
// -----------------------------------------

and SetupAdapter() be
[
	DoFunc(fControl, 1)	//Reset Orbit
	for i=0 to 15 do		//Set inkwell
		[
		DoFunc(fXY, i lshift 12)
		DoFunc(fInk, -1)
		]

	AdCommand(adBufferReset)
	AdCommand(adSetScales+BitScale lshift 9+MotorScale lshift 6)
	AdCommand(adBitClock+BitClock)
	AdCommand(adMotorSpeed+MotorSpeed)
	AdCommand(adLineSyncDelay+LineSyncDelay)
	AdCommand(adPageSyncDelay+PageSyncDelay)
	AdCommand(adVideoGate+VideoGate)

// Try to empty Orbit buffers to clear them.  This is to minimize
// toner dumping on first (blank) page.
	let dump=vec 256
	for i=0 to 31 do DoFunc(fReadBlock, 256, dump)
]

// -----------------------------------------
// Utility functions.
// -----------------------------------------

and AddQEntry(color, paperAction) be
[
	ROSCommand(PAppendQueueEntry)
	ROSCommand(PSetVideo)
	ROSCommand(color)
	ROSCommand(paperAction)
]

and FeedASheet() be switchon printerDevice into
[
case printerDover:
	ROSCommand(adExternalCommand1+1)
	ROSCommand(adExternalCommand1)
	endcase
case printerSequoia:
	ROSCommand(60000b)
	ROSCommand(60001b)
	endcase
case printerPimlico: case printerPuffin:
	if useStandardQueue then	//Use standard 3 or 4 color queue
	 ROSCommand((nPrinterColors eq 3? #63346,#63306))
	endcase
]

//Wait for page sync to equal given value.
// Returns true if there was nothing unexpected

and AwaitPageSync(ps) = valof
[
	if Debug then resultis true
	let tim=@RTC
	[
	if (@RTC-tim) gr 5*27 then resultis false
	DoFunc(fControl, 0)	//Read ROS special status (1st four bits of status word 0)
	let f=Func!9
	if f<<STATUS.ROSStatusBit0 eq ps then resultis true
	] repeat
]


// -----------------------------------------------------------------
// FailureCode = ROSCheck(running)
// ------------------------------------------------------------------

// Analyze ROS status
// FailureCode = 0 if there are no malfunctions, ROS is ready to print
//			otherwise, an error code (see below)
// 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.

// The failure codes divide into ranges:
// 1-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-149	Inherent problems (e.g., insufficient bandwidth somewhere) that are
//		not curable by mere mortals. Attempts to improve the situation should be
//		abandoned.
// 150-199	Inherent problems encountered, but they did not terminate printing. One
//		should expect that one or more pages were improperly imaged.

// The machine-dependent control table is constructed using the
// CT structure.
// invert means that the bit is on when the condition is not a problem (e.g., "Ready")
// failureCode should be reported when the condition specified by the bitIndex'th
//		bit inROS status word wordIndex is a problem.
// (Pimlico uses a different format: right byte is status code returned from machine,
//	left byte is status code returned from ROSCheck

// ROSCheck (cont)

and ROSCheck(running) = valof
[

let ros8 = ROSStatus(8)
let ros9 = ROSStatus(9)
let ros12 = ROSStatus(12)
let ros13 = ROSStatus(13)

// Following stuff is for Pimlico, Puffin
let malFnStatus=ros13&#377
let seqStatus = ros13 rshift 8
let subSeq=seqStatus&7
let mainSeq = (seqStatus rshift 3)&7
let mode = seqStatus rshift 6
let printSeq=selecton mainSeq into
	[
	case 1: case 2: Pwarmup
	case 3: Pstandby
	case 4: selecton subSeq into
		[
		case 0: Pprinting
		case 1: Pendofrun
		case 2: Plowpaper
		case 3: Pbadfeed
		case 4: Pbadstrip
		case 5: Pclearing
		default: Pillegal1
		]
	case 5: PmalfClear
	default: Pillegal2
	]
if printerDevice eq printerPimlico then
	printSeq=selecton seqStatus into
	[
	case 1: case 2: Pwarmup
	case 3: Pstandby
	case 4: Pprinting
	case 5: Pendofrun
	case 6: Plowpaper
	case 7: Pbadfeed
	case 8: Pbadstrip
	case 9: Pclearing
	case 10: PmalfClear
	default: Pillegal2
	]

let ready = selecton printerDevice into
	[
		// Power on, no malfunction, paper tray OK, fuser warm 
	case printerDover: (ros9&#2100) eq #2000  &  (ros8&#2004) eq 4
		// Ready, no malfunction or no malfunction (depends on running)
	case printerSequoia: (ros9&(running? #100, #300)) eq 0
		// Standby or no malfunction (depends on running)
	case printerPimlico: ros12 ge 0 & malFnStatus eq 0
 		& (printSeq eq Pstandby %
 		(running & (printSeq eq Pprinting % printSeq eq Pclearing)))
	case printerPuffin: valof
		 [	//if running, expect Pstandby
			//else, either Pprinting or Pendofrun
			let expected=running?
				((printSeq eq Pendofrun)%(printSeq eq Pprinting)),
				(printSeq eq Pstandby)
			let rdy=expected&//Puffin in right place
				(malFnStatus eq 0)&//no malfunction
				((ros12&#17377) eq rosCount)&//no queue problems
				(mode eq 1)	//remote print mode
			unless rdy do resultis false
			if printSeq ne Pstandby then resultis true
			//we're ready, and in standby: check for scanning
			let lineCount = ROSStatus(7)&#170000
			let rtc=@RTC
			let scanTicks=32//# of ticks in 4 scans
			until (@RTC-rtc) ge scanTicks do [ ]
			resultis (ROSStatus(7)&#170000) ne lineCount
		 ]
	default: true
	]

if ready % Debug resultis 0

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

let tab = selecton printerDevice into
   [
   case printerDover: table [
	10*Code   +   8*Word +  5*Bit // PTDisorder-H Check paper tray
	20*Code   +   8*Word + 10*Bit // 27LS-H Jam -- paper feed
	21*Code   +   8*Word + 14*Bit // LostPower-H Lost engine power
	22*Code   +   9*Word +  1*Bit // PhotoCellOut-H Jam--paper on drum
	23*Code   +   9*Word +  2*Bit // PreSeq-H Jam -- startup sequence
	24*Code   +   9*Word +  6*Bit // 38LS-H Jam--paper on fuser roll
	25*Code   +   9*Word + 10*Bit // 3LS-H Jam--paper on drum
	 1*Code   +   8*Word + 13*Bit + Invert // ReadyTemp-H Fuser not warm
	26*Code   +   9*Word +  5*Bit + Invert // ACMonitor-H Engine not powered up
	27*Code   +   9*Word +  9*Bit // Malfunction-H unexplained malfunction
	0  ]
   case printerSequoia: table [
	 2*Code   +   8*Word + 13*Bit // Fuser not warm
	11*Code   +   8*Word +  5*Bit // Check paper tray
	30*Code   +   9*Word +  2*Bit // Jam
	31*Code   +   9*Word +  9*Bit // Unexplained Malfunction
	 3*Code   +   9*Word +  8*Bit // Not ready
	0  ]
   case printerPuffin:
   case printerPimlico: table [ // error code,, machine code
	13 lshift 8 + #32 // 1A 2/2 low paper.
//	50 -- M6800 is in wait state (see below)
//	51 -- command sync count error (see below)
	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.
	 6 lshift 8 + Pwarmup	// PrintSequence = poweron or warmup
	14 lshift 8 + Plowpaper	// PrintSequence = low paper
	64 lshift 8 + Pbadfeed	// PrintSequence = bad feed
	65 lshift 8 + Pbadstrip	// PrintSequence = bad strip
	66 lshift 8 + PmalfClear	// PrintSequence = malfunction clearing
	67 lshift 8 + Pillegal2	// Illegal print sequence code
	68 lshift 8 + Pillegal1	// Illegal print subsequence code
// 69 -- T events missed (see below)
	0 + Pprinting			//Will set code=0
	0 + Pendofrun
	0 + Pclearing
	0 + Pstandby
	0  ]
   default: table [ 0 ]

// Codes generated elsewhere:
//	  8 -- no specific malfunction detected, but something is wrong
//	 18 -- Orbit timed out -- page sync never arrived, or engine died or got behind
//	 97 -- (Dover) page did not arrive at Count-H
//	 98 -- Orbit invalid band entry
//	 99 -- Timeout of unknown origin
//	100 -- Disk got behind
//	101 -- Orbit got behind, but page terminated normally
//	102 -- Orbit got behind, had to quit early
//	103 -- Left over table got too large. Please report to fixer.

   ]

let bits = table [ #100000;
	  #40000;	  #20000;	  #10000
	   #4000;	   #2000;	   #1000
	    #400;	    #200;	    #100
	     #40;	     #20;	     #10
	      #4;	      #2;	      #1	]

// ROSCheck (cont)

// Condition interpreter

let code = 0
let pDevice=(printerDevice eq printerPimlico % printerDevice eq printerPuffin)
if pDevice then
	test rosSyncError then code = 51
	 or test ROSStatus(12) < 0 then code = 50
	 or test malFnStatus eq 0 then malFnStatus=printSeq
	 or if #200 le malFnStatus & malFnStatus le #240 then code = 69 // 80-A0 T events missed

unless code while @tab do
	[
	let entry = @tab
	test pDevice ne 0
	 ifnot if (  ( ROSStatus(entry<<CT.wordIndex) & (bits!(entry<<CT.bitIndex)) )
	    xor entry<<CT.invert  ) ne 0 then
		[ code = entry<<CT.failureCode; break ]
	 ifso if malFnStatus eq (entry&#377) then [ code = entry rshift 8; break ]
	tab = tab+1
	]

// Perhaps this condition ought to be promoted to a higher value
unless code do code = 8	// No specific malfunction found -- what's happening?
resultis code
]