{File name:  Halftone.mc
Description: Halftone opcode
Created by: MRR 12-Nov-86 10:30:06
Last edited: MRR  5-Jan-87 16:32:15 changed mapSrcBuf to mapHTlineBuf
Last edited: JPM 18-Dec-86  9:54:35 move subroutine return points here from Halftone.dfn
Last edited: MRR 17-Dec-86 13:43:26 }

{ 	Copyright (C) 1986 by Xerox Corporation.  All rights reserved.	}

{ This opcode leaves the calling arguments on the stack during all operations. Link registers are used to determine which pixel of the source word to use (Link 7) and whether to invert the destination word (Link 6). This page contains the code to map all source, destination and halftone cell line pages. Should a page fault occur, control is passed to the appropreiate handler. This file was printed with the following XDE Executive command:

		>print gacha8/f /-a Halftone.mc
 }

{Return points from subroutine RestoreSysRegs}
Set[L0.HTinterrupt,	0];
Set[L0.HTDone,		1];
Set[L0.HTfault,		2];

@HALFTONE: Q ← 0F							,c1, at[08,10,ESCAn];
	Q ← ErrnIBnStkp and Q						,c2; {load stack size & mask}
	[] ← Q - 4, NibCarryBr						,c3; {if stk < 12 ... }

	[] ← uHTscanLineLength, ZeroBr, BRANCH[$, PopToDeath]		,c1;  {..raise a stack error.}
	rhVD ← uHTdstHi, BRANCH[$, EarlyDone]				,c2;
	VD ← uHTdstLo, CALL[SaveSysRegs]				,c3;
	
mapBuffs: bitWidth ← uHTscanLineLength, L1 ← 1				,c1;
	L3 ← L3.HTdstMap						,c2;
	DBit ← uHTdstBitOffset, CALL[ComMap]				,c3;
	
	{ComMap subroutine here {10 1/3 Clicks}				,c1-c1;}
	uHTdstRealLo ← BARealALo, BRANCH[$,HTDstFault]		,c2,at[L3.HTdstMap,10,ComMapRet];
	Q ← BARealAHi, L3 ← L3.HTlineMap				,c3;
	
	uHTdstRealHi ← Q, L1 ← 0					,c1;
	[] ← uHTinvertReq, NZeroBr					,c2;
	L6 ← 0, Rbb0 ← 0FF + 1, BRANCH[$, mapHTlineBuf]			,c3;

	VD ← uHTlineLo, GOTO[mapHTlineBufCont]				,c1;
mapHTlineBuf: VD ← uHTlineLo						,c1; {link 6 gets set here}
mapHTlineBufCont: rhVD ← uHTlineHi					,c2;
	wordWidth ← uHTlineLength, CALL[ComMapWord]			,c3;
	
	{ComMap subroutine here {10 1/3 Clicks}				,c1-c1;}
	rHTAddrLo ← BARealALo, BRANCH[$,HTlineFault]		,c2,at[L3.HTlineMap,10,ComMapRet];
	Q ← BARealAHi, L1 ← 0						,c3;
	
	rhHTAddrHi ← Q LRot0						,c1;
	DByte ← uHTsrcByteOffset, L3 ← L3.HTsrcMap, NZeroBr		,c2;
	VD ← uHTsrcLo, L7 ← 0, BRANCH[$, prePreMapScanLine]		,c3;
	
	uHTmaskN ← 0, GOTO[premapScanLine]				,c1; {save rhL reg}
prePreMapScanLine: uHTmaskN ← 0						,c1; {link 7 gets set here}
premapScanLine: rhVD ← uHTsrcHi						,c2;
mapScanLine: byteWidth ← uHTscanLineLength, CALL[ComMapByte]		,c3; {clears rDstPixel}
	
	{ComMap subroutine here {10 1/3 Clicks}				,c1-c1;}
	uHTsrcRealLo ← BARealALo,rDstOrgWord ← 0,BRANCH[$,HTSrcFault],c2,at[L3.HTsrcMap,10,ComMapRet];
	BARealALo ← BARealAHi						,c3;

	uHTsrcRealHi ← BARealALo, rDstPixel ← 0				,c1;
	rDstBitCount ← uHTdstBitOffset, ZeroBr				,c2;
	rhAddrHi ← uHTdstRealHi, BRANCH[$, ZeroOffset]			,c3;
	
	rAddrLo ← uHTdstRealLo						,c1;
	Noop								,c2;
	Noop								,c3;

initialCase: Q ← 10 - rDstBitCount, L0 ← L0.iCaseMask			,c1;
	rhTemp ← Q LRot0, CALL[RMaskN]					,c2;
	{RMaskN subroutine here {1 Click}				,c3-c2;}
	uHTmaskN ← Q ← ~rMaskN						,c3, RMaskNRet[L0.iCaseMask];
	
	MAR ← [rhAddrHi, rAddrLo + 0]					,c1;
	rDstBitCount ← uHTdstBitOffset					,c2;
	rDstOrgWord ← MD and Q						,c3;

ZeroOffset: rhAddrHi ← uHTsrcRealHi					,c1;
	rAddrLo ← uHTsrcRealLo						,c2;
	rHTOffset ← uHTlineOffset					,c3; {clear dest pixels accum}

{ This is the main loop. A source word is read every other pixel. After a pixel has been halftoned, a decision is made whether to write out the resultant word. This decision is based upon two factors. One, if the number of pixels halftoned equals the scan line length (the final case), or whether up to sixteen pixels have been halftoned (general case). The halftone cell line is also managed within this loop. }

preHalfToneLoop: MAR ← [rhAddrHi, rAddrLo + 0], GOTO[HTLoopCont]	,c1;
HalfToneLoop: MAR ← rAddrLo ← [rhAddrHi, rAddrLo + 1]			,c1;
HTLoopCont: uHTsrcRealLo ← rAddrLo, BRANCH[$, SrcPgX, 1]		,c2;
	rSrcWord ← MD							,c3; {get 2 pixels}
	
getHTelement: MAR ← [rhHTAddrHi, rHTAddrLo + rHTOffset]			,c1;
grabHTelement: Q ← 0FF, BRANCH[$, getNewHTpage,1]			,c2;
	rHalfToneElement ← MD						,c3; {HT element}
	
	Rbb0 ← uHTscanLineLength, L7Disp				,c1;
	Rbb0 ← Rbb0 - 1, DISP2[selectByte, 02]				,c2;
	rSrcPixel ← rSrcWord and Q, GOTO[HTaPixel] 			,c3, at[3,4,selectByte];
	rSrcPixel ← rSrcWord LRot8 and Q				,c3, at[2,4,selectByte];
	
HTaPixel: Q ← rSrcPixel - rHalfToneElement				,c1;
	rDstPixel ← rDstPixel DLShift1					,c2;
	Q ← rHTOffset + 1						,c3;

	[] ← uHTlineLength xor Q, ZeroBr				,c1;
	uHTlineOffset ← rHTOffset ← Q, BRANCH[HTpad, $]			,c2;
	uHTlineOffset ← 0, rHTOffset ← 0, GOTO[ReadyToWrite]		,c3;
HTpad:	uHTscanLineLength ← Rbb0					,c3;{save scan line count}

ReadyToWrite: rhAddrHi ← uHTdstRealHi					,c1;
	rDstBitCount ← rDstBitCount + 1, NibCarryBr			,c2; {for general case}
	rAddrLo ← uHTdstRealLo, L6Disp, BRANCH[$, generalCase]		,c3;

	uHTscanLineLength ← Rbb0, CANCELBR[$, 1]			,c1;
	[] ← Rbb0, ZeroBr, L0 ← L0.fCaseData				,c2; {for final case}
	Q ← 10 - rDstBitCount, BRANCH[$, finalCase]			,c3;

ChkSrcByte: rAddrLo ← uHTsrcRealLo, L7Disp				,c1;
	rhAddrHi ← uHTsrcRealHi, DISP2[testByte, 02]			,c2; {which byte was used ?}
	L7 ← 1, Rbb0 ← 0FF + 1, GOTO[getHTelement]			,c3, at[2,4,testByte];
	L7 ← 0, Rbb0 ← 0FF + 1, GOTO[HalfToneLoop]			,c3, at[3,4,testByte];

{ This code handles writing the bilevel pixels. Upon completion a test is made for the done state. If not done, return control to the main loop. }

finalCase: rhTemp ← Q LRot0						,c1;{0 bit offset,SL < 16}
	Q ← rDstPixel, CALL[LRotateN]					,c2; {rotate dst pixels}
	{LRotateN subroutine here {1 2/3 Clicks}			,c3-c1;}
	rMaskN ← uHTmaskN or rMaskN					,c2, LRotateNRet[L0.fCaseData];
	uHTmaskN ← rMaskN						,c3;

	MAR ← [rhAddrHi, rAddrLo + 0]					,c1;
	rDstPixel ← rTemp						,c2;
	rDstOrgWord ← MD and rMaskN, L6Disp				,c3;

generalCase: MAR ← [rhAddrHi,rAddrLo + 0], DISP2[invertByte, 02]	,c1;{0 bit offset,SL>=16}
	MDR ← rDstOrgWord or rDstPixel, GOTO[chkForDone]		,c2, at[3,4,invertByte];
	rMaskN ← ~uHTmaskN						,c2, at[2,4,invertByte];
	Q ← ~rDstPixel and rMaskN					,c3;
	
	MAR ← [rhAddrHi,rAddrLo + 0]					,c1;
	MDR ← rDstOrgWord or Q						,c2;
chkForDone: [] ← Rbb0, ZeroBr, L0 ← L0.HTDone				,c3; {test for done}
	
	rDstPixel ← 0, uHTscanLineLength ← Rbb0, BRANCH[$, HTDone]	,c1; {save new scanline length}
	Rbb0 ← 0FF + 1							,c2;
	rDstBitCount ← 0, uHTdstBitOffset ← 0				,c3;
	
	rAddrLo ← rAddrLo + 1, PgCarryBr				,c1; {test for page cross}
	uHTdstRealLo ← rAddrLo, BRANCH[$, DstPgX]			,c2;
	uHTmaskN ← 0, rDstOrgWord ← 0, GOTO[ChkSrcByte]			,c3;

	{ Dest, Source, and HalfTone Line Page Crosses are handled here }

DstPgX: rAddrLo ← uHTdstLo						,c3;

	rAddrLo ← rAddrLo and ~0FF, L0 ← L0.HTinterrupt       		,c1;
	rAddrLo ← rAddrLo + Rbb0, rhAddrHi ← uHTdstHi, CarryBr		,c2;
	uHTdstLo ← rAddrLo, BRANCH[HTmapDst, $]				,c3;
	
	Q ← rhAddrHi + 1, LOOPHOLE[byteTiming]				,c1;
	uHTdstHi ← Q							,c2;
	rhAddrHi ← Q LRot0						,c3;

HTmapDst: Map ← [rhAddrHi, rAddrLo], MesaIntBr				,c1; {map new dst page}
	Q ← 0FF, BRANCH[$, HandleInterrupt]				,c2;
	rhAddrHi ← MD, rAddrLo ← MD					,c3;
	
	MAR ← rAddrLo ← [rhAddrHi, 0 + 0]				,c1;
	uHTdstRealLo ← rAddrLo						,c2; {get new real dest addr}
	Q ← rhAddrHi							,c3;
	
	uHTdstRealHi ← Q						,c1;
	Noop								,c2;
	uHTmaskN ← 0, rDstOrgWord ← 0, GOTO[ChkSrcByte]			,c3;

SrcPgX: rAddrLo ← uHTsrcLo						,c3;

	rAddrLo ← rAddrLo and ~0FF					,c1;
	rAddrLo ← rAddrLo + Rbb0, rhAddrHi ← uHTsrcHi, CarryBr		,c2;
	uHTsrcLo ← rAddrLo, BRANCH[HTmapSrc, $]				,c3;
	
	Q ← rhAddrHi + 1, LOOPHOLE[byteTiming]				,c1;
	uHTsrcHi ← Q							,c2;
	rhAddrHi ← Q LRot0						,c3;

HTmapSrc: Map ← [rhAddrHi, rAddrLo]					,c1; {map new src page}
	Noop								,c2;
	rhAddrHi ← MD, rAddrLo ← MD					,c3;
	
	MAR ← rAddrLo ← [rhAddrHi, 0 + 0]				,c1;
	uHTsrcRealLo ← rAddrLo						,c2;
	Q ← rhAddrHi							,c3;
	
	uHTsrcRealHi ← Q						,c1;
	Noop								,c2;
	GOTO[preHalfToneLoop]						,c3;

getNewHTpage: rNewHToffset ← rHTAddrLo					,c3;

	rAddrLo ← uHTlineLo						,c1;
	rAddrLo ← rAddrLo and ~0FF					,c2;
	rAddrLo ← rAddrLo + Rbb0, rhAddrHi ← uHTlineHi, CarryBr		,c3;
	
	Q ← rhAddrHi + 1, LOOPHOLE[byteTiming], BRANCH[preHTmapLine, $]	,c1;
	rhAddrHi ← Q LRot0, GOTO[HTmapLine]				,c2;
preHTmapLine: Noop							,c2;
HTmapLine: Noop								,c3;

mapLine: Map ← [rhAddrHi, rAddrLo]					,c1; {map new HT line page}
	rNewHToffset ← rNewHToffset + rHTOffset				,c2;
	rhAddrHi ← MD, rAddrLo ← MD					,c3;
	
	MAR ← rAddrLo ← [rhAddrHi, 0 + 0]				,c1;
	Noop								,c2;
	rNewHToffset ← rNewHToffset and 0FF				,c3;
	
	MAR ← [rhAddrHi, rAddrLo + rNewHToffset], GOTO[grabHTelement]	,c1;

	{ Mesa returns, Interurupts and Page Faults and Stack errors are handled here }

	{ Mesa returns are handled here }
	
HTDone:	CALL[RestoreSysRegs]						,c2;
	stackP ← 0, GOTO[Bank1NxtInstc1]			,c3, at[L0.HTDone,4,SysRegsRet];
	
EarlyDone: stackP ← 0, GOTO[Bank1NxtInstc1]				,c3;


	{ Interurupts are handled here }
	
HandleInterrupt: Q ← uHTsrcRealLo and Q, CALL[RestoreSysRegs]		,c3;

	Rbb0 ← -Rbb0						,c1, at[L0.HTinterrupt,4,SysRegsRet];
	Rbb0 ← uHTsrcLo and Rbb0, L7Disp				,c2;
	Rbb0 ← Rbb0 or Q, DISP2[intUpdSBoffset, 2]			,c3;
	
	uHTsrcLo ← Rbb0, rDstPixel ← rDstPixel + 1, GOTO[doInt]		,c1, at[2,4,intUpdSBoffset];
	Rbb0 ← Rbb0 + 1, CarryBr					,c1, at[3,4,intUpdSBoffset];
	uHTsrcLo ← Rbb0, BRANCH[preDoInt, $]				,c2;
	rhAddrHi ← uHTsrcHi						,c3;
	
	Q ← rhAddrHi + 1, LOOPHOLE[byteTiming]				,c1;
	uHTsrcHi ← Q							,c2;
preDoInt: Noop								,c3;
	
	Noop								,c1;
doInt:	TOS ← uHTinvertReq						,c2;
	uHTsrcByteOffset ← rDstPixel, GOTO[Bank1Interrupt]		,c3;
	
	
	{ Page Faults are handled here }
	
HTDstFault: uFaultParm0 ← VD, GOTO[HandlePageFault]			,c3;
HTlineFault: uFaultParm0 ← VD, GOTO[HandlePageFault]			,c3;
HTSrcFault: uFaultParm0 ← VD, GOTO[HandlePageFault]			,c3;

HandlePageFault: VD ← rhVD						,c1;
	uFaultParm1 ← VD						,c2;
	T ← Q, L0 ← L0.HTfault						,c3;
	
	CALL[RestoreSysRegs]						,c1;
	TOS ← uHTinvertReq, GOTO[Bank1Fault]			,c2, at[L0.HTfault,4,SysRegsRet];


	{ Stack errors are raised here }

PopToDeath: pop, CANCELBR[$, 1], GOTO[PopToDeath]			,c*;


	{ these subroutines save and restore system registers used by this opcode }

SaveSysRegs: ULsave ← L							,c*;
	UrG ← G, L ← rhL						,c*;
	UrhLsave ← L, GOTO[mapBuffs]					,c*;
	
RestoreSysRegs: rhL ← UrhLsave						,c*;
	L ← ULsave, pRet0						,c*;
	G ← UrG, DISP2[SysRegsRet]					,c*;