{
 File name:  Raven.mc
 Description: Raven LSEP (Low Speed Electronic Printer) microcode
 Author: Pitts PXJ   ,
 Created: March 14, 1980,
 Last Edited:
 	Julio JxC     ,  7-Feb-85 17:07:05 Added CopyRight notice.
	Neil NXS         , 16-Jan-85 11:59:45 Rewrite to even out overhead between
	  scan line involving bandswitch and scan line without bandswitch.  This 
	  allows a common version of microcode to drive Ravens, Banshee and Fuji 
	  model 35s, with varying clock rates.
	AeF AEF     ,  6-Feb-84 17:37:47  Move initialization of displayBase1 to
          start of code.
	AeF AEF     , 13-Oct-83 11:04:28  Change references for IOPage move.
	CrF   CRF, 21-Oct-82 18:25:35  Rewrite vertical registration code to allow
	  shorter delay between line sync and first data bit sent.
	AeF AEF     , August 3, 1982  1:54 PM  Change CSB Offset names
	AeF AEF     , July 6, 1982  11:42 AM  Move WakeupMaskOffset definition to
	  Dandelion.df
	RXG   , July 30, 1981  11:51 PM: added ClrDPRq & PCtl← after endBand
	  so that hardware fix to allow slower LSEP's works.  (Note that the click
	  at endBand can be eliminated if the click before uses nibCarry.)
	PXO     , March 23, 1981  5:25 PM: Remove some unneeded CANCELBR's
	PXJ   , November 25, 1980  2:14 PM
}
  
{	Copyright (C) 1980, 1981, 1982, 1983, 1984, 1985 by Xerox Corporation.  All rights reserved. }

{
WARNING:  this microcode is extremely timing dependent.  Don't attempt to change it without fully understanding the timing constraints.
Notes:
1. There is a parameterized delay loop that controls the amount of JYW   video data between the rising edge of the line sync signal and the first actual video data bit (pixel) sent to the printer.  This delay is adjusted by the operator to align the printed image with the paper on a particular printing device.  The unit of delay at the Face level is 16 pixels.  After line sync is sensed, a 16-bit counter is enabled in the DLion Raven Controller.  This counter is clocked by the video data clock from the printer, and is used to cause a wakeup every 16 pixels.
2. There are six DLion clicks of overhead every time we switch from one band of 16 scan lines to the next band.  The hardware wakeup register remains set after a data wakeup until the microcode executes a ClrDPRq.  By not executing a ClrDPRq in the six click band overhead code, it is possible to get those six clicks to run in six consecutive rounds, taking only 2.057 microseconds each, rather than 16*printerDataRate each (for Raven each full tick of the video clock is .278 microseconds, so data wakeups happen every 4.48 microseconds) if we waited for the next data wakeup each time.  There is an "Overhead" loop to equalize the band switch/non band switch time between line sync and the registartion delay loop.
3. This overhead must be accounted for as part of the registartion delay, as the Raven Spec calls for a minimum delay of 70 pixels. For Raven the ratio of rounds to data wakeups is close to 2 to 1, so the six rounds of band overhead get counted as three data wakeups, or two counts through the delay loop.  This means the minimum delay for Raven is three data wakeups, or about 48 pixels, counting the delay loop exit click.  For Banshee the ratio is about 7 to 2, (pixel clock is .444 microseconds, data wakeups happen every 7.104 microseconds) so minimum delay is 32 pixels.  This is acceptable within the allowances of the Banshee registation switches.  Due to the design of the DLion Raven Controller, the first data wakeup comes about 4 pixels after line sync, so the actual minimum delay possible is 4 pixels longer.
}

{
Printer Control register on X-Bus
~ClearLineActive	8	zero inhibits data wakeups and forces video to all
				JYW   until next line sync
ForceRequest		4	forces printer wakeups every round
~VideoWhite		2	zero forces video to JYW  
EnablePrinter		1	enables printer and memory refresh task wakeups
}

Set[EndActiveScanCtl, 1];
Set[EndBandScanCtl, 5];
Set[StartScanCtl, 9];
Set[StartActiveCtl, 0B];

{Raven CSB  (read once per page, immediately after code detects run#0)
	run		non-zero=> index of 1st band record (displayBase0
			relative), read then zeroed
	overrun		non-zero=> over ran band buffer
	interruptMask	Mesa process stuff
	bandSize	number of lines in a band
	active scan	number of words in line buffer actually sent to printer
	line size	number of words in line buffer
	tab		number of words skipped from line sync before sending image
			to printer
	scans		number of blank lines sent to printer at start of page}

{Raven band record
	pointer		real address of band (displayBase1 relative)
	next		index of next band record (displayBase0 relative)
	status		0 => empty, #0 => full, <0 => last band}

SetTask[1];
StartAddress[PrinterInit];

PrinterInit:
	PCtl← EndActiveScanCtl, CancelDisp[$]{wakeup on next line sync}	,c1;
nextPage:
	displayBase0 ← IOPageHigh, ClrDPRq {band buffer in first 64K}	,c2;
	rD0 ← uIOPage							,c3;

pageGap:
	MAR← [displayBase0, rD0 + RavenRunOffset] {wait for run # 0}	,c1;
	MDR← 0, CancelPgCross[$, 0], LOOPHOLE[wok] {clear immediately}	,c2;
	rD1← MD								,c3;

	uThis← rD1, ZeroBr						,c1;
	rD1← 0A, BRANCH[startPage, $]		{rD1← AltUaddr index}	,c2;
	PCtl← EndActiveScanCtl, GOTO[pageGap]				,c3;

startPage:
	Noop								,c3;

	{clear overrun}
	rD0 ← MAR ← [displayBase0, rD0 + RavenOverrunOffset]		,c1;	
	MDR ← 0, CancelPgCross[$, 0], LOOPHOLE[wok]			,c2;
	displayBase1 ← 0						,c3;

loadParam:
	MAR← [displayBase0, rD0+1], rD0← rD0+1	{load parameters}	,c1;
	uScans← rD0, CancelPgCross[$]					,c2;
	rD0← MD								,c3;

	Ybus← rD1, rD1← rD1+1, NibCarryBr, AltUaddr			,c1;
	uyParamBase← rD0, BRANCH[$, fixUTabCnt]				,c2;
	rD0← uScans, GOTO[loadParam]					,c3;

fixUTabCnt:
	rD1← uTabCnt							,c3;

	rD1← rD1-3	{for compatability with previous versions}	,c1;
	uTabCnt← rD1							,c2;

skipLines:
	rD0← rD0-1, ZeroBr			{loaded uScans last}	,c3;

	BRANCH[$, firstBand]			{line sync wakeup}	,c1;
	PCtl← EndActiveScanCtl, GOTO[skipLines]				,c2;

firstBand:
	GOTO[thisBand]			{avoid allocation constraints}	,c2;

nextBand:
	Noop								,c2;
thisBand:
	rD0← uThis							,c3;

	MAR← [displayBase0, rD0], rD0← rD0+1	{line sync wakeup}	,c1;
	CancelPgCross[$]						,c2;
	rD1← MD					{pointer to band}	,c3;

	MAR← [displayBase0, rD0], rD0← rD0+1				,c1;
	uLineAddress← rD1, CancelPgCross[$]				,c2;
	rD1← MD					{next band}		,c3;

	MAR← [displayBase0, rD0], uBandStatus← rD0			,c1;
		{save address for later}
	uThis← rD1, CancelPgCross[$]					,c2;
	rD1← MD					{status of this band}	,c3;

	[]← rD1, ZeroBr							,c1;
	rD1← uLineAddress, BRANCH[$, RavenOverrun]			,c2;
	rD0← uBandSize							,c3;

	uLineCount← rD0, GOTO[startLine1]				,c1;

startLine:
	PCtl← EndActiveScanCtl						,c1;
	rD0← uLineSize							,c2;
	rD1← uLineAddress						,c3;

	rD1← rD1+rD0				{line sync wakeup}	,c1;
	uLineAddress← rD1						,c2;
	rD0← 3			{3 clicks more overhead if band switch}	,c3;

overhead:
	Noop								,c1;
	rD0← rD0-1, ZeroBr		{equalize band switch overhead}	,c2;
	BRANCH[overhead, $]						,c3;

	Noop								,c1;
startLine1:
	PCtl← StartScanCtl						,c2;
	rD0← uTabCnt							,c3;

idle:
	rD0← rD0-1, ZeroBr			{tab in to active area}	,c1;
	BRANCH[$, startActive], ClrDPRq					,c2;
	Noop, GOTO[idle]						,c3;

startActive:
	PCtl← StartActiveCtl		{stop forcing video JYW  }	,c3;

scan:
	MAR← [displayBase1, rD1+0], ClrDPRq				,c1;
	MDR← rD0{= zero}, rD1← rD1+1, PgCarryBr				,c2;
	POData← MD, BRANCH[scan, endLine]				,c3;

endLine:
	rD1← uLineCount, ClrDPRq		{play out last word}	,c1;
	rD1← rD1-1, ZeroBr						,c2;
	uLineCount← rD1, BRANCH[startLine, endBand]			,c3;

endBand:
	Noop								,c1;
	PCtl← EndBandScanCtl						,c2;
	rD0← uBandStatus						,c3;

	MAR← [displayBase0, rD0+0]		{mark band empty}	,c1;
	MDR← rD1				{... and get next band}	,c2;
	rD1← MD					{negative=> last band}	,c3;

	rD0← uWP							,c1;
	rD0← uInterruptMask or rD0, MesaIntRq				,c2;
	uWP← rD0		{initiate band complete interrupt}	,c3;

	Noop								,c1;
	Noop								,c2;
endBand1:
	[]← rD1, NegBr				{neg => last band done}	,c3;
	PCtl← EndActiveScanCtl, BRANCH[nextBand, nextPage]		,c1;

RavenOverrun:
	rD1← uBandStatus						,c3;

	rD1← rD1-2							,c1;
	uThis ← rD1				{back up pointer}	,c2;
	rD0← uIOPage							,c3;

	MAR← [displayBase0, rD0 + RavenOverrunOffset]	{set overrun}	,c1;
	rD1← MDR ← 1, CancelPgCross[endBand1, 0], LOOPHOLE[wok]		,c2;


{Memory Refresh Task}

SetTask[3];
StartAddress[RefreshGo];

	Set[HalfFieldSize, 0E0];

RefreshGo:
	uClockBits ← 0, CancelDisp[$]					,c1;
	displayBase2 ← IOPageHigh					,c2;
RefreshField:
	rD2 ← uIOPage							,c3;

	MAR ← [displayBase2, rD2 + WakeupMaskOffset]			,c1;
	Noop, CancelPgCross[$, 0]					,c2;
	rD2 ← MD							,c3;

	uPWakeup ← rD2							,c1;
	Noop								,c2;
	Noop								,c3;

{This code looks this way because uWP must be updated within one click.}
	rD2 ← uWP							,c1;
	rD2 ← uPWakeup or rD2, MesaIntRq				,c2;
	uWP ← rD2							,c3;

	rD2 ← LShift1 HalfFieldSize					,c1;
	uRefreshLine ← rD2						,c2;
	ClrRefRq							,c3;

Refresh:
	rD2 ← uClockLow, Refresh					,c1;
	rD2 ← rD2 + 1, CarryBr						,c2;
	uRefreshTemp ← rD2, BRANCH[$, RefreshCarry]			,c3;

	Refresh								,c1;
	Noop								,c2;
Refresh1:
	uClockLow ← rD2							,c3;

	rD2 ← uRefreshLine, ZeroBr					,c1;
	rD2 ← rD2 - 1, BRANCH[$, RefreshField]				,c2;
	uRefreshLine ← rD2, ClrRefRq, GOTO[Refresh]			,c3;

RefreshCarry:
	rD2 ← uClockHigh, Refresh					,c1;
	rD2 ← rD2 + 1							,c2;
	Noop								,c3;

	uClockHigh ← rD2						,c1;
	rD2 ← uRefreshTemp, GOTO[Refresh1]				,c2;