TITLE[EOMStarHiTask];
%
Edited by Tom Henning Feb 2, 1983 fix inkwell glitch bug by checking for z-plane blocks, and then checking for inkwell blocks
Edited by Tom Henning Jan 18, 1983 streamlined eomXExtract code
Edited by Tom Henning Jan 4, 1983 add Trapezoid code
Edited by Tom Henning June 30, 1982 changed edit history to reflect Star graphics only

From EOMHiTask.mc Rubicon version 6/30/82, to be extended for Star graphics
Previously edited by Steve Dashiell, Ron Joiner, and Ed Fiala
Originally written by Steve Dashiell
%
SetTask[eomHiTask];

eomHiTaskInit:
LoadPage[eomHiTaskPage], At[eomHiTaskInitLoc];
GoToP[eomHiTaskSleep];

OnPage[eomHiTaskPage];

*Send the IOSTROBE to tell the hardware to consider the request satisfied.
eomHiTaskSleep:
IOSTROBE, CALL[eomHiTaskSwitch];

%High Task Running Code.
Any wakeup at the high task level will come to eomHiRun.
The instruction following eomHiRun tests IOATTEN to decide if the request
is for normal data or for something special, and branches accordingly.
Bump the eomDataTransferOffset assuming that the wakeup is for compressed
data. This will be unbumped if it is not.
%
eomHiRun:
eomDataTransferOffset ← T ← (eomDataTransferOffset) + (20C);
*The IOATTEN test must be in the second instruction. Set up for a slow
*branch on the font/image bit of the eomActiveAttributes. This will be used
*only on the normalXfer branch.
DBLGOTO[eomSpecialXfer,eomNormalXfer,IOATTEN], LU ← LDF[eomActiveAttributes,15,1];

%
********************
Normal Data Transfer Request - - NO IOATTEN
This is where all requests for normal bitmap data come.
(First compressed data request for any object is an IOATTEN request)
%
********************

eomNormalXfer:
*Initiate the data transfer to the device and branch on font/image.
DBLGOTO[eomFont,eomImage,ALU=0],
IOFETCH16[eomDataTransferBaseA,ADD[hiTaskIOoffset,inputChannelBuffer]];

eomFont:
*The font data is a linear list; no ring management is required.
GOTO[eomHiTaskSleep];

eomImage:

*The images are rings. Decrement the blocks remaining to be read
*and check for sector completely read.
DBLGOTO[eomEndSector,eomNoEndSector,R<0], eomBlocksRemaining ← (eomBlocksRemaining) - 1;

eomEndSector:
*this code runs if all the blocks in one sector of the ring have been read.
*Check to see if the ring has wrapped. The terminal address is specfied
*in blocks to allow rings up to 4 megabits. Rings larger than 1 megabit are
*not currently supported.
T ← (LSH[eomTerminalBlockAddress,4]) - T;
*decrement the count of sectors filled and make wrap test.
GOTO[eomNoRingWrapA,ALU#0], eomSectorsFilled ← (eomSectorsFilled) - 1;
eomDataTransferOffset ← (ZERO);
eomDataTransferOffset ← (eomDataTransferOffset) - (20C);
eomNoRingWrapA:
*Reset the eomBlocksRemaining value to indicate a full sector.
CALL[eomInitBlocksRemaining];
*Set up the transfer offset for the proper image quad block in the CSB
*and stash the block. Also pick up the wakeup reasons.
T ← (LSH[eomActiveAttributes,2]);
*The wakeup reason bit selection is done in a subroutine so there will be a task
*switch immediately prior to the call on eomHiTaskModifyWakeupReasons, due
*to its length.
DISPATCH[eomActiveAttributes,16,2], CALL[eomSelectWakeupReasonSub];
CALL[eomHiTaskModifyWakeupReasons];
*Check to see if there are any more sectors of data available.
GOTO[eomNoMoreDataAvailable,R<0], A ← eomSectorsFilled;
GOTO[eomHiTaskSleep];
eomNoMoreDataAvailable:
*Send the bit to indicate that we are starting on a non valid data sector.
T ← imageUnderFlow, CALL[eomHiTaskModifyWakeupReasons];
GOTO[eomHiTaskSleep];
eomNoEndSector:
GOTO[eomHiTaskSleep];

%
********************
Special Data Transfer Request - - IOATTEN
This is where all requests for data other than bitmap data come.
%
********************

eomSpecialXfer:
*The special, IOATTEN branch. Get the dispatch field from the hardware.
INPUT[eomDispField,wakeUpType];
*Load the active attributes. This will be used in eomFirstDataBlock
*if that is the branch taken. It fills in time that would otherwise
*be wasted due to the INPUT interlock on the dispatch.
T ← eomActiveAttributes;
*Force the interlock to work and do the dispatch
DISPATCH[eomDispField,14,4], LU ← eomDispField;
*Reset the eomDataTransferOffset to the proper value.
DISP[eomFirstDataBlock], eomDataTransferOffset ← (eomDataTransferOffset) - (20C);

%
********************
SPECIAL TRANSFER BRANCH 0
When the first block of data for a character or image is requested, the transfer
base, and possibly ring control information must be set up. The type of setup
required is indicated by the microcode attributes field in the first word in the
parameter block.
%
********************

eomFirstDataBlock:
*First data block for this image. Set up to test whether the attributes
*have changed since the last item. NOTE - - THIS CODE MAKES THE IMPLICIT
*ASSUMPTION THAT IF THE ATTRIBUTES HAVE NOT CHANGED, THEN NEITHER THE
*PREVIOUS ITEM NOR THIS ONE ARE NOT NON-RESIDENT IMAGES.
*T was initialized with eomActiveAttributes just after the INPUT in the
*specialXfer path.
LU ← (LDF[eomParmBlock0,15,3]) - T, AT[hiTaskWakeupDispatch];
*Set up T with the block pointer value for use if the no change
*path is taken. Mask off the z-Plane information.
T ← (eomBlockPointer) AND NOT (17C),
DBLGOTO[eomNoAttributesChange,eomAttributesChange,ALU=0];
eomNoAttributesChange:
*Do the data transfer of the first data block.
IOFETCH16[eomDataTransferBaseA,ADD[hiTaskIOoffset,inputChannelBuffer]];
IOSTROBE, TASK;
*The IOSTROBE will give T a change to be stored after the IOFETCH16.
eomDataTransferOffset ← T;
*This code assumes that the item MUST be a font item if there was no
*attributes change. Hence, no check for the font/image bit is made,
*no ring checks are made, etc.
GOTO[eomHiRun];
eomAttributesChange:
*Task out here because it is less convenient later.
CALL[eomHiTaskSwitch];
*First, we must check to see if the previous object was an image. If
*so, the eomBlocksRemaining value and the eomDataTransfer value must
*be saved away.
LU ← LDF[eomActiveAttributes,15,1];
*Perform the branch to distinguish between image and not image on the
*previous object. Set up T for use if the branch is not taken.
GOTO[eomPreviousItemNotImage,ALU=0], T ← (eomDataTransferOffset) + (20C);
*Previous item was an image, stash the eomBlocksRemaining and
*eomImageXferOffset values. Active attributes here belongs to the item
*which has just finished printing.
eomImageXferOffset ← T;
T ← (6C);
T ← (LSH[eomActiveAttributes,1]) AND T, CALL[eomHiTaskSwitch];
PSTORE2[eomParameterTransferBaseA,eomBlocksRemaining];

eomPreviousItemNotImage:
*Place the attributes for the current item in T.
T ← (eomParmBlock0) AND (7C);
*Pick up the most significant attribute bit for the character about to begin.
*This differentiates between font bank or image and will always
*come from the parameter block most recently transferred to the hardware.
LU ← LDF[eomParmBlock0,15,1];
*Branch on the font/image bit, and set the active attributes to the values
*for the current item.
DBLGOTO[eomProcessFontBank,eomProcessImageSelect,ALU=0], eomActiveAttributes ← T;

eomProcessFontBank:
*Fetch the base address of font bank 0 into the data transfer offset reg pair.
PFETCH2[eomCsbBaseLo,eomDataTransferBaseA,fontPointerOffset];
CALL[eomHiTaskSwitch];
*Incrementing the font pointer by 0,1,2, or 3 megabits requires the
*high and low byte of the second register in the register pair to have
*0,1,2 or 3 respectively added to both bytes.
*Construct the pointer increment. T already contains the eomActiveAttributes.
T ← (LSH[eomActiveAttributes,10]) OR T;
*Add the increment value to the high half of the register.
eomDataTransferBaseB ← (eomDataTransferBaseB) + T;
T ← (eomBlockPointer) AND NOT (17C);
eomDataTransferOffset ← T, GOTO[eomNoSectorAdjustA];

eomProcessImageSelect:
*ODD BRANCH
*Fabricate the offset to use to fetch the double word pointer to the
*image buffer. Note--eomActiveAttributes bit 15 is always one on this
*dispatch branch, so the lshift 2 will be 0,4,8, or 12 plus 16.
*On the image branch of the dispatch, it is assumed that the attributes
*will always have changed since the last character.
T ← (LSH[eomActiveAttributes,2]);
*Fetch the quadword from the CSB defining this image.
PFETCH4[eomCsbBaseLo,eomDataTransferBaseA];
T ← (6C);
*EomActiveAttributes here belongs to the item which is about to start printing.
T ← (LSH[eomActiveAttributes,1]) AND T, TASK;
*Fetch eomBlocksRemaining and eomImageXferOffset for the image about to begin.
*The task switch after the PFETCH2 will leave volatile state in
*eomImageXferOffset, but since lo task cannot get in while hi task is
*trying to run, this works.
*(eomImageXferOffset is used as a temporary in eomHiTaskModifyWakeupReasons.)
PFETCH2[eomParameterTransferBaseA,eomBlocksRemaining];
*Pick up the count of sectors full.
T ← LDF[eomRingChar,0,sectorsFilledFieldWidth];
*Extract the block pointer information from the "blockPointer" register.
*Result is returned in T.
eomSectorsFilled ← T, USECTASK, CALL[eomExtractBlockPointer];
*Store the masked block pointer in T as the eomDataTransferOffset.
eomDataTransferOffset ← T;
*Put the actual block address in T.
T ← RSH[eomDataTransferOffset,4], CALL[eomHiTaskSwitch];
*Compute the count of blocks prefetched but not used in the previous
*band of thisimage.
T ← (RSH[eomImageXferOffset,4]) - T;
*Check to see if the microcode offset wrapped but the hardware pointer didn’t.
GOTO[eomNoAddTerminalCount, ALU>=0];
*This branch taken if the microcode pointer wrapped but the hardware pointer
*did not. The terminal count (plus one, to account for the full ring size) is
*added into the result to make it positive.
T ← (eomTerminalBlockAddress) + T + 1;
eomNoAddTerminalCount:
*Add this to the number of blocks believed to be remaining, before adjustment
*for the number of blocks prefetched but not used.
*Use eomHiTaskSwitch rather than TASK to be sure that the value actually
*gets stored properly.
eomBlocksRemaining ← T ← (eomBlocksRemaining) + T;
*If the result is larger than a sector, we must back up past a sector
*boundary. Pick up the blocks per sector value from eomRingChar and subtract
*the value computed above.
LU ← (LDF[eomRingChar,blocksPerSectorStartBit,blocksPerSectorFieldWidth]) - T;
*Perform the branch. Also set up T with one plus the number of blocks in a
*sector, in case the branch is not taken. The equal case of this branch means
*that the back up was to, but not past a sector boundary and no adjustment is
*required.
GOTO[eomNoSectorAdjust, ALU>=0],
T ← (LDF[eomRingChar,blocksPerSectorStartBit,blocksPerSectorFieldWidth]) + 1;

*Using T from the previous instruction, load eomBlocksRemaining with (a sector
*size plus 2) less than it had before. The plus 2 is because of the -2 terminal
*count on eomBlocksRemaining.
eomBlocksRemaining ← (eomBlocksRemaining) - T - 1;
*Increment the count of sectors full. The application software is never allowed
*to have overwritten the last sector emptied. Also make the call to
*modify the value of sectors full saved in eomRingChar.
eomSectorsFilled ← (eomSectorsFilled) + 1, CALL[eomInitBlocksRemainingA];
*Set up T with the displacement and store the image ring quad block in the CSB.
T ← LSH[eomActiveAttributes,2];
PSTORE4[eomCsbBaseLo,eomDataTransferBaseA];

eomNoSectorAdjust:
*Pick up the dataTransferOffset in T once again.
T ← eomDataTransferOffset;
eomNoSectorAdjustA:
*Set up for the branch test at eomNormalXfer
LU ← LDF[eomActiveAttributes,15,1], GOTO[eomNormalXfer];

%
********************
SPECIAL TRANSFER BRANCH 1
When the hardware first requests a state save, the code comes here. Additional blocks
of data for the same state save are handled by the COCBRequest branch.
%
********************

eomOCBRequest:
*state save request
eomOldListWrite←T←(eomOldListWrite)+(20C),TASK,AT[hiTaskWakeupDispatch,2];
*Bump the eomOldBlocksReturned value to account for the data transfer.
(eomOldBlocksReturned) ← (eomOldBlocksReturned) + 1;
*Overflow condition is indicated by read and write pointers being equal.
LU ← (eomOldListRead) - T;
GOTO[eomOldListOverflow,ALU=0],
IOSTORE16[eomParameterTransferBaseA,ADD[hiTaskIOoffset,outputChannelBuffer]];
*Check for old list ring wrap;
T ← (eomFragments) - (40C); * at least 2 blocks left
LU ← (eomOldListWrite) - T; * or else reset the write pointer
GOTO[eomOCBRequestSleep,ALU<0], T ← (eomState) AND (newListSizeMask);
*Ring has wrapped, fix the write pointer
eomOldListWrite ← T, GOTO[eomHiTaskSleep];
eomOCBRequestSleep:
GOTO[eomHiTaskSleep];
eomOldListOverFlow:
*There is no recovery from old list overflow. Pause the hi task, but do
*not set the hitask paused status bit. This means that the low task will
*never attempt to restart the hi task. Also, set the bit in the CSB wakeup
*reasons to indicate workspace overflow.
T ← (workSpaceOverflow);
CALL[eomHiTaskSwitch];
CALL[eomHiTaskModifyWakeupReasons];
CALL[eomDropDead];
GOTO[eomHiRun];

%
********************
SPECIAL TRANSFER BRANCH 2
%
********************

eomCOCBRequest:
*continue state save option
GOTO[eomOCBRequest], AT[hiTaskWakeupDispatch,4];

%
********************
SPECIAL TRANSFER BRANCH 3
When the hardware asks for the first block of parameters for a character, this code runs.
It is necessary to check which list, new or old to use, whether z-Planes have changed etc.
The numbered cases following a label indicate the various ways to reach that label.
%
********************

eomIPBRequest:
*check to see which list is being processed
DBLGOTO[eomProcessNewList,eomProcessOldList,R<0], LU ← eomState,
AT[hiTaskWakeupDispatch,6];

eomProcessNewList:
*ODD BRANCH

*1.)Processing new list upon entry (FROM eomIPBRequest).

*2.)Processing old list upon entry, AND old list is empty
*(FROM eomOldListEmptyA).

*check if the new list has any blocks in it
DBLGOTO[eomNewListEmpty,eomNewListNotEmpty,R<0], LU ← eomNewBlocksFull;

eomNewListEmpty:
*ODD BRANCH

*1a.)Processing new list upon entry, AND new list is empty
*(FROM eomProcessNewList).

*1b.)Processing old list upon entry, AND old list is empty,
*AND new list is empty (FROM eomProcessNewList).

CALL[eomHiTaskSwitch];
LU ← (eomState) AND (bandDone);
*check if the low task has processed a band end block entry
DBLGOTO[eomLoTaskDone,eomLoTaskNotDone,ALU#0];

eomLoTaskDone:
*ODD BRANCH

*1a.)Processing new list upon entry, AND new list is empty,
*AND lo task is done (FROM eomNewListEmpty).

*1b.)Processing old list upon entry, AND old list is empty,
*AND new list is empty, AND lo task is done (FROM
*eomNewListEmpty).

*low task is done and the new list is empty. Check to see if the
*old list is also empty, by fast branch on eomOldBlocksFull.
*Also load T with the value which will be needed in bandFinished.
DBLGOTO[eomBandFinished,eomBandNotFinished,R<0], A ← eomOldBlocksFull;

eomBandFinished:
*ODD BRANCH

*1a.)Processing new list upon entry, AND new list is empty,
*AND lo task is done, AND old list is empty (FROM eomLoTaskDone).

*1b.)Processing old list upon entry, AND old list is empty,
*AND new list is empty, AND lo task is done, AND [REDUNDANT
*TEST - - old list is empty] (FROM eomLoTaskDone).

*Old and new lists are both empty. Ship the dummy end of band character.
*Load up the first two words of the EOB character for subsequent data
*transfers.

eomState ← (eomState) AND NOT (eomLastOldNewWasZ); * clear ZMarker flags
T ← (20C), TASK;
*Force the next band to start processing in the old list.
eomState ← (eomState) AND NOT (workingFromNewList);
PFETCH2[eomParameterTransferBaseA,eomParmBlock0];
*Check to see if this it is real time or parametric mode.
LU ← (eomState) AND (parametricModeFlag);
*Branch, and set up for the test on eob already processed.
DBLGOTO[eomParametricHi,eomRealTimeHi,ALU#0], LU ← (eomState) AND (freezeEOBChar);

eomParametricHi:
*ODD BRANCH

*1a.)Processing new list upon entry, AND new list is empty,
*AND lo task is done, AND old list is empty, AND band
*was parametric (FROM eomBandFinished).

*1b.)Processing old list upon entry, AND old list is empty,
*AND new list is empty, AND lo task is done, AND [REDUNDANT
*TEST - - old list is empty], AND band was parametric (FROM
*eomBandFinished).

*In parametric mode, the low task does not attempt to do any look ahead
*block generation. It will awaken when the band switch indication arrives.
*The high task must send a dummy character to shut down the parameter
*request for the
*first character of the second band, and to allow bitmap data transfer to
*begin. Set new list stuff to dummy in the non-printing character stored
*in the third block of the workspace.
*Branch on EOB character already sent.
*Fake the new list read pointer. This will be incremented by 20B prior to
*the transfer. This points to the first of the two non-printing dummys.
GOTO[eomParametricFreeze, ALU#0], eomNewListRead ← T ← (20C);
*Prohibit zPlane switching on the dummy character. The parameter block
*for the character must indicate zPlane = 0.
eomOldZNewZ ← (0C);
*Indicate two blocks in new list, both non-printing dummys.
*Send the EOB parameter block. Cannot task out until after the IOSTROBE
*has been sent or the hardware breaks. IOSTROBE must arrive before the
*IOFETCH16 is complete.
*Indicate that a subsequent end of band character must not be sent.
eomNewBlocksFull ← (1C);
eomState ← (eomState) OR (freezeEOBChar);
IOFETCH16[eomParameterTransferBaseA,ADD[hiTaskIOoffset,inputParameterBuffer]],
GOTO[eomHiTaskSleep];
eomParametricFreeze:
*An EOB character has already been sent for this band.
GOTO[eomWaitOnLoTask];

eomRealTimeHi:
*EVEN BRANCH

*1a.)Processing new list upon entry, AND new list is empty,
*AND lo task is done, AND old list is empty, AND band
*was real time (FROM eomBandFinished).

*1b.)Processing old list upon entry, AND old list is empty,
*AND new list is empty, AND lo task is done, AND [REDUNDANT
*TEST - - old list is empty], AND band was real time (FROM
*eomBandFinished).

*Reinitialize the new list parameters. This is required following the use of
*the end of band character.
LoadPage[eomLoTaskPage];
*Set up the lo task activation flag.
eomDispField ← (setLoTaskRun), CALL[eomInitNewList];
*Output the lo task activation.
OUTPUT[eomDispField,loTaskCommandReg];
*Reset the previous z-plane reference.
eomOldZNewZ ← (0C);
*Stop the hi task by writing 0 to the start bit and reinitialize the
*new list ring parameters.
T ← (20C), TASK;
*This instruction serves to interlock the OUTPUT, as well as setting up the
*zero value for later use.
eomDispField ← (0C);
eomXferEOBBlock:
IOFETCH16[eomParameterTransferBaseA,ADD[hiTaskIOoffset,inputParameterBuffer]];
IOSTROBE;
*Stop the hi task by writing 0 to the start bit.
OUTPUT[eomDispField,opcStartupOut];
*Clear the band done indication.
eomState ← (eomState) AND NOT (bandDone), TASK;
*Interlock the OUTPUT.
eomDispField ← eomDispField;
GOTO[eomHiRun];

eomBandNotFinished:
*EVEN BRANCH

*1a.)Processing new list upon entry, AND new list is empty,
*AND lo task is done, AND old list is not empty (FROM eomLoTaskDone).

*1b.)ABSURD CASE - - CAN NEVER OCCUR
*Processing old list upon entry, AND old list is empty,
*AND new list is empty, AND lo task is done, AND old list
*is not empty (FROM eomLoTaskDone).

*new list is empty but old list is not. Send parameter block from old list.
*Can jump into the middle because we know already that the old list is not
*empty and the z-Plane change check is not necessary, since only the old
*list remains, and it is sorted. It is necessary to fetch the first two
*words of the parameter block before doing the GOTO.
T ← (eomOldListRead) + (20C);
PFETCH2[eomParameterTransferBaseA,eomParmBlock0], CALL[eomHiTaskSwitch];
*
GOTO[eomOldListTransfer];
eomState ← (eomState) AND NOT (workingFromNewList), GOTO[eomOldListTransfer];

eomLoTaskNotDone:
*EVEN BRANCH

*1a.)Processing new list upon entry, AND new list is empty,
*AND lo task is not done (FROM eomNewListEmpty).

*1b.)Processing old list upon entry, AND old list is empty,
*AND new list is empty, AND lo task is not done (FROM
*eomNewListEmpty).

*new list is empty because the low task fell behind. It is not done yet.
*Check if a switch to the old list is allowable. The condition for this
*is if the z-Plane for the first block on the old list is the same as the
*most recently transferred block.
*First, check to see whether the old list is empty.
DBLGOTO[eomWaitOnLoTask,eomSwitchToOldList,R<0], LU ← eomOldBlocksFull;

eomWaitOnLoTask:
*ODD BRANCH

*1a.)Processing new list upon entry, AND new list is empty,
*AND lo task is not done processing the band, AND old list
*is empty (FROM eomLoTaskNotDone).

*1b.)Processing old list upon entry, AND old list is empty,
*AND new list is empty, AND lo task is not done, AND
*[REDUNDANT TEST - - old list is empty] (FROM eomLoTaskNotDone).

*2.)Processing new list upon entry, AND new list is empty,
*AND lo task is not done processing the band, AND old list
*is not empty, AND current old list z-Plane does not match
*previous z-Plane (FROM eomNoSwitchList).

*3.)Processing old list upon entry, AND old list is not empty,
*AND old list z-Plane > previous z-Plane, AND new list is
*empty, AND lo task is not done (FROM eomLoTaskNotDoneA).

*4a.)Processing new list upon entry, AND new list is empty,
*AND lo task is done, AND freezeEOB character condition
*is set (FROM eomBandFrozen). This occurs only in parametric
*mode, and then only when the second request for an EOB
*character for the band is made.

*4b.)Processing old list upon entry, AND old list is empty,
*AND new list is empty, AND lo task is done, AND freezeEOB
*character condition is set (FROM eomBandFrozen). This occurs only
*in parametric mode, and then only when the second request for an EOB
*character for the band is made.

*the old list is empty, we must wait on the low task to put a block in the
*new list. Set the hi Task pause. Low task will remove the pause when it
*puts a block in the new list.
CALL[eomPauseHiTask];
*Must not go to eomHiTaskSleep, because no IOSTROBE should be issued. The
*request is not yet satisfied.
GOTO[eomHiRun];

eomSwitchToOldList:
*EVEN BRANCH

*1a.)Processing new list upon entry, AND new list is empty,
*AND lo task is not done processing the band, AND old list
*is not empty (FROM eomLoTaskNotDone).

*1b.)ABSURD CASE - - CAN NEVER OCCUR
*Processing old list upon entry, AND old list is empty,
*AND new list is empty, AND lo task is not done, AND
*{ABSURD - - old list is not empty} (FROM eomLoTaskNotDone).

*the old list is not empty. Now check the z-Plane to see if a switch is
*possible.
*load T with the offset of the word with the z-Plane info.
T ← (eomOldListRead) + (20C);
PFETCH2[eomParameterTransferBaseA,eomParmBlock0], CALL[eomHiTaskSwitch];
T ← LDF[eomOldZNewZ,0,10];
LU ← (LDF[eomOldZNewZ,10,10]) - (T);
DBLGOTO[eomSwitchList,eomNoSwitchList,ALU>=0];
* switch to Old List iff NewListZ>=OldListZ

eomSwitchList:
*EVEN BRANCH

*1.)Processing new list upon entry, AND new list is empty,
*AND lo task is not done processing the band, AND old list
*is not empty, AND current old list z-Plane matches previous
*z-Plane (FROM eomSwitchToOldList).

*actually switch to the old list and resume processing.
*set hi task status to indicate old list processing. We can branch into the
*middle of the old list processing because we know that the old list is not
*empty, and there is no change in the z-Plane information.
eomState ← (eomState) AND NOT (workingFromNewList), GOTO[eomOldListTransfer];

eomNoSwitchList:
*ODD BRANCH

*1.)Processing new list upon entry, AND new list is empty,
*AND lo task is not done processing the band, AND old list
*is not empty, AND current old list z-Plane does not match
*previous z-Plane (FROM eomSwitchToOldList).

*z-Plane will not allow the switch.
GOTO[eomWaitOnLoTask];

eomNewListNotEmpty:
*EVEN BRANCH

*1a.)Processing new list upon entry, AND new list is not empty
*(FROM eomProcessNewList).

*1b.)Processing old list upon entry, AND old list is empty,
*AND new list is not empty (FROM eomProcessNewList).

*2.)Processing old list upon entry, AND old list is not empty,
*AND old list z-Plane > previous z-Plane, AND new list is not
*empty, AND old list z-Plane > new list z-Plane. The previous
*z-Plane value has been adjusted to the current value. (FROM
*eomOldToNewSwitch).

*the new list has blocks in it. Check for a Z-plane switch.
*first, bump the offset. Cannot save the bumpped offset until we are sure
*which list the block is really going to be sent from.
T ← (eomNewListRead) + (20C);
PFETCH2[eomParameterTransferBaseA,eomParmBlock0];

LU ← eomOldBlocksFull, GOTO[eomNewListTransferA,R<0];
* if Old List empty, don’t check z-planes
T ← LDF[eomOldZNewZ,10,10];
LU ← (LDF[eomOldZNewZ,0,10]) - (T); * OldZ-NewZ
skip[ALU>=0];
eomState ← (eomState) AND NOT (workingFromNewList), GOTO[eomProcessOldList];
* process Old List if OldListZ<NewListZ
nop;
eomNewListTransferA:
LoadPage[eomHiTaskPage2];
gotoP[eomNewListTransfer];

OnPage[eomHiTaskPage2];
eomNewListTransfer:
*EVEN BRANCH

*1a.)Processing new list upon entry, AND new list is not empty
*AND no z-Plane switch since last block (FROM eomNewListNotEmpty).

*1b.)Processing old list upon entry, AND old list is empty,
*AND new list is not empty, AND no z-Plane switch since
*last block (FROM eomNewListNotEmpty).

*1c.)Processing old list upon entry, AND old list is not empty,
*AND old list z-Plane > previous z-Plane, AND new list is not
*empty, AND old list z-Plane > new list z-Plane. The previous
*z-Plane value has been adjusted to the current value. (FROM
*eomNewListNotEmpty).

*2a.)Processing new list upon entry, AND new list is not empty
*AND there is a z-Plane switch since last block, AND the
*old list is empty (FROM eomOldListEmpty).

*2b.)Processing old list upon entry, AND old list is empty,
*AND new list is not empty, AND there is a z-Plane switch
*since last block, AND [REDUNDANT TEST - - the old list is
*empty] (FROM eomOldListEmpty).

*3.)Processing new list upon entry, AND new list is not empty
*AND there is a z-Plane switch since last block, AND the
*old list is not empty, AND the new list z-Plane value is
*<= the old list z-Plane value (FROM eomNoNewToOldSwitch).

lu ← LDF[eomParmBlock1,16,1], Skip[R Even];
lu ← (eomNewBlocksFull)-1, goto[eomInkwellBlockNew];
lu ← (eomState) AND (eomLastNewWasZ), goto[eomSendNewBlock,alu=0];

eomState ← (eomState)And Not(eomLastNewWasZ), goto[eomZMarkerNew,alu=0]; * was the last a z-block ?
goto[eomSendNewBlock]; * yes, send the z-block
eomZMarkerNew:
T ← LDF[eomOldZNewZ,0,10];
lu ← (LDF[eomParmBlock0,0,10]) - (T);
T ← LDF[eomParmBlock0,0,10], Skip[alu<0];
eomState ← (eomState) AND NOT (workingFromNewList);
* switch to Old List if NewListZ>=OldListZ
eomOldZNewZ ← (eomOldZNewZ) AND (177400C);
eomOldZNewZ ← (eomOldZNewZ) OR (T);
* update z-plane value for New List
LoadPage[eomHiTaskPage];
eomState ← (eomState) OR (eomLastNewWasZ), gotoP[eomIPBRequest]; * set flag

eomSendNewBlock:
eomNewBlocksFull ← (eomNewBlocksFull) - 1;
*Log the removal of the block from the new list.
(eomNewListRead) ← T ← (eomNewListRead)+(20C);*reload the offset
*CANNOT task out after this IOFETCH because the IOSTROBE must
*arrive at the hardware before the transfer completes.
eomSendNewBlockXfer:
IOFETCH16[eomParameterTransferBaseA,ADD[hiTaskIOoffset,inputParameterBuffer]];
LoadPage[eomHiTaskPage];
gotop[eomHiTaskSleep];

eomInkwellBlockNew:
T ← (eomNewListRead), goto[eomUpdateInkwellNew,alu>=0];
* there should be a Rulette Block already generated by LoTask
LoadPage[eomHiTaskPage];
gotoP[eomWaitOnLoTask];

eomUpdateInkwellNew:
eomTrapezoid0 ← T;
T ← (eomTrapezoid0)+(42C), CALL[eomXExtract];
* update Word Pointer

eomSendInkwellRuletteNew:
T ← (eomNewListRead)+(40C), CALL[eomSendRulette];

eomParmBlock0 ← eomParmBlock0, goto[eomContinueTrapezoidNew,R Even];
eomUpdateTrapezoidNew:
T ← (eomNewListRead), goto[eomUpdateTrapezoid];
*
eomParmBlock1 ← T, goto[eomUpdateTrapezoid];
* obtain eomNextY and eomNextHeight

eomContinueTrapezoidNew:
eomParmBlock0 ← (eomParmBlock0)-(10C); * decrement width left by 10
eomParmBlock1 ← (eomParmBlock1)+(1000C), goto[eomInkwellAllDoneNew,alu<0];
* increment X coordinate
T ← (eomNewListRead)+(20C), goto[eomInkwellBandDoneNew,alu<0];
* band is done if carry occurs

*
T ← (eomNewListRead)+(20C);
PStore2[eomParameterTransferBaseA,eomParmBlock0];
T ← (eomNewListRead)+(40C), GOTO[eomInkwellContinueNew];
*inkwell not done, update InkwellRule Information Block

eomInkwellBandDoneNew:
*
eomState ← (eomState) OR (workingFromNewList);
T ← (eomNewListRead), goto[eomMoveInkwellBlocks];

*update pointers to reflect 2 New Blocks consumed, 2 Old Blocks added
eomFinishMoveNew:
eomNewListRead ← T ← (eomNewListRead)+(40C), CALL[eomInkwellBlockGet];
eomOldListWrite ← T ← (eomOldListWrite)+(40C), CALL[eomInkwellBlockPut];
* update the eomNewBlocksFull to account for 2 New Blocks consumed
eomNewBlocksFull ← (eomNewBlocksFull) - (2C);

* check for an Old List ring wrap (Write Pointer)
T ← (eomFragments) - (40C), CALL[eomCheckOldListWrite];

eomInkwellAllDoneNewA:
T ← (eomNewListRead), goto[eomInkwellContinueNew]; * avoid LoadPage gotcha

eomInkwellAllDoneNew:
eomNewListRead ← T ← (eomNewListRead)+(40C);
eomNewBlocksFull ← (eomNewBlocksFull) - (2C), goto[eomInkwellAllDoneNewA];

eomInkwellContinueNew:
*
PFetch4[eomParameterTransferBaseA,eomParmBlock0];
PFetch2[eomParameterTransferBaseA,eomParmBlock0];
* restore eomParmBlock0 for data transfer attributes testing
LoadPage[eomHiTaskPage];
gotoP[eomHiRun];

OnPage[eomHiTaskPage];
eomProcessOldList:
*EVEN BRANCH

*1.)Processing old list upon entry.

*check if the old list has any blocks in it
DBLGOTO[eomOldListEmptyA,eomOldListNotEmptyA,R<0], LU ← eomOldBlocksFull;

eomOldListEmptyA:
*ODD BRANCH

*1.)Processing old list upon entry, AND old list is empty
*(FROM eomProcessOldList).

*if the old list is empty, all that can be done is to switch back to the
*new list and continue processing
eomState ← (eomState) OR (workingFromNewList), CALL[eomHiTaskSwitch];
GOTO[eomProcessNewList];

eomOldListNotEmptyA:
*EVEN BRANCH

*1.)Processing new list upon entry, AND new list is not empty
*AND there is a z-Plane switch since last block, AND the
*old list is not empty, AND the new list z-Plane value is
*> the old list z-Plane value (FROM eomNewToOldSwitch).

*2.)Processing old list upon entry, AND old list is not empty
*(FROM eomProcessOldList).

*if the old list is not empty, we must check for a z-Plane change
T ← (eomOldListRead) + (20C);
PFETCH2[eomParameterTransferBaseA,eomParmBlock0], CALL[eomHiTaskSwitch];
*extract the z-Plane info
T ← LDF[eomOldZNewZ,0,10];
LU ← (LDF[eomOldZNewZ,10,10]) - (T);
GOTO[eomZPlaneSwitchA,ALU<0];
* look at New List iff NewListZ<OldListZ

eomOldListTransfer:
*EVEN BRANCH

*1.)Processing new list upon entry, AND new list is empty,
*AND lo task is done, AND old list is not empty. (FROM
*eomBandNotFinished)

*2.)Processing new list upon entry, AND new list is empty,
*AND lo task is not done processing the band, AND old list
*is not empty, AND current old list z-Plane matches previous
*z-Plane (FROM eomSwitchList).


*3a.)Processing new list upon entry, AND new list is not empty
*AND there is a z-Plane switch since last block, AND the
*old list is not empty, AND the new list z-Plane value is
*> the old list z-Plane value, AND [REDUNDANT CHECK - - the
*old list z-Plane value is <= the new list z-Plane value]
*(FROM eomOldListNotEmptyA).

*3b.)Processing old list upon entry, AND old list is not empty,
*AND old list z-Plane = previous z-Plane (FROM eomOldListNotEmptyA).

*4.)Processing old list upon entry, AND old list is not empty,
*AND old list z-Plane > previous z-Plane, AND new list is
*empty, AND the lo task is done (FROM eomLoTaskDoneA).

*5.)Processing old list upon entry, AND old list is not empty,
*AND old list z-Plane > previous z-Plane, AND new list is not
*empty, AND old list z-Plane <= new list z-Plane (FROM
*eomNoOldToNewSwitch).

T ← LDF[eomParmBlock1,16,1], goto[eomInkwellBlockOld,R Odd];
* inkwell Information Block?

*We must check to see if the block being sent is an image, and if so, whether
*the block pointer in the parameter block requires adjustment due to an
*imaginal input ring wrap during the last band. If an adjustment is required,
*do it.

eomNotInkwellOld:
LU ← LDF[eomParmBlock0,15,1];*pick up the font/image bit.
*If the block is not for an image, branch. Also, set up T in case the
*branch is not taken.
GOTO[eomNoImageWrap,ALU=0], T ← (eomParmBlock0) AND (7C);
*
*The ring wrap computation must be done here as a part of the parameter block
*transfer because the block pointer must be adjusted (unwrapped) before the
*parameter block is sent to the hardware.
*
*Construct a pointer to the terminal count for this image in the CSB.
*This automatically comes out in the high CSB because of the 1 bit in position
*15B which indicates image.
eomTemp ← T, CALL[eomHiTaskSwitch];
eomTemp ← (LSH[eomTemp,2]);
eomTemp ← T ← (eomTemp) + (2C);
*Fetch the terminal count into eomDispField.
PFETCH1[eomCsbBaseLo,eomDispField];
*Now fetch the microcode transfer offset (eomImageXferOffset) from location
*1,3,5, or 7 of the junk area depending upon which image.
T ← (7C);
T ← (RSH[eomTemp,1]) AND T, TASK;
PFETCH1[eomParameterTransferBaseA,eomImageXferOffset];
*Load T with the terminal count (which is one block less than the actual size of
*the ring. Add one block to this value.
T ← (eomDispField) + 1;
*EomBlockPointer contains words, not blocks, and must be shifted.
T ← (RSH[eomBlockPointer,4]) - T;
*If the result is negative, the block pointer had not really gone beyond
*the end of the ring buffer. If the branch is not taken, the result was
*positive and the block pointer needs unwrapping. Stash T in eomDispField
*so it can be shifted if the branch is not taken.
GOTO[eomHardwarePointerInBounds, ALU<0], eomTemp1 ← T;
eomProcessWrap:
*The result was positive; the block pointer needs unwrapping.
*Shift the modified block pointer into the high 12 bits.
T ← LSH[eomTemp1,4];
*Get the Z-Plane value and OR it into its slot in the word.
*
eomBlockPointer ← (LDF[eomBlockPointer,14,4]) OR T;
eomBlockPointer ← T;
T ← (eomOldListRead)+(20C);
*Store the first 2 words of the parameter block to effect the modification of
*the block pointer.
PSTORE2[eomParameterTransferBaseA,eomParmBlock0], GOTO[eomNoImageWrap];
eomHardwarePointerInBounds:
*The hardware pointer was within the bounds of the data ring. It is still
*possible that the hardware pointer wrapped. Check this by looking at the
*separation between the hardware pointer and the saved value of the microcode
*pointer.
*
*Get the block pointer down to a block value.
T ← RSH[eomBlockPointer,4];
*Convert the microcode pointer to a block value and obtain the difference
*between microcode pointer and hardware pointer.
eomImageXferOffset ← (RSH[eomImageXferOffset,4]) - T;
*If the result is less than 0, the hardware pointer is within the ring
*(from the preceeding test) and above the microcode pointer. This is legal,
*indicating that the microcode pointer wrapped but the hardware pointer has
*not.
GOTO[eomNotReallyWrapped, ALU<0], LU ← (eomImageXferOffset) - (22C);
*The result of the preceeding ALU operation will be greater than zero only if
*the separation of the microcode and hardware pointers is 16 blocks or greater.
*If this occurs, the hardware pointer has wrapped and must be adjusted.
GOTO[eomNotReallyWrappedA, ALU<0], T ← (eomDispField) OR (170000C);
*This instruction loads T with the count of blocks between the top end of the
*ring and the megabit boundary. This value must be added to the hardware value
*to achieve a correct pointer.
T ← (ZERO) OR NOT T;
T ← (RSH[eomBlockPointer,4]) + T, CALL[eomHiTaskSwitch];
*Load eomDispField with the correct block value of the pointer, and go process
*the actual wrap adjustment and storage of the modified value.
eomTemp1 ← T, GOTO[eomProcessWrap];
eomNotReallyWrapped:
GOTO[eomNoImageWrap];
eomNotReallyWrappedA:
GOTO[eomNoImageWrap];
eomNoImageWrap:
*This is the entry point from eomCIPBRequest - - IOATTEN branch 4.
lu ← LDF[eomParmBlock1,16,1];
lu ← (eomState) AND (eomLastOldWasZ), goto[eomSendOldBlock,alu=0];

eomState ← (eomState)And Not(eomLastOldWasZ), goto[eomZMarkerOld,alu=0]; * was the last a z-block ?
goto[eomSendOldBlock]; * yes, send the z-block
eomZMarkerOld:
T ← LDF[eomOldZNewZ,10,10];
lu ← (LDF[eomParmBlock0,0,10]) - (T);
T ← (eomParmBlock0) AND (177400C), Skip[alu<0];
eomState ← (eomState) OR (workingFromNewList);
* switch to New List if OldListZ>=NewListZ
eomOldZNewZ ← LDF[eomOldZNewZ,10,10];
eomOldZNewZ ← (eomOldZNewZ) OR (T);
* update z-plane value for Old List
eomState ← (eomState) OR (eomLastOldWasZ), goto[eomIPBRequest];

eomSendOldBlock:
eomOldBlocksFull ← (eomOldBlocksFull) - 1;
*log the removal of the block from the old list
(eomOldListRead) ← T ← (eomOldListRead)+(20C);*reload the offset
*CANNOT task out after this IOFETCH because the IOSTROBE must
*arrive at the hardware before the transfer completes.
eomSendOldBlockXfer:
IOFETCH16[eomParameterTransferBaseA,ADD[hiTaskIOoffset,inputParameterBuffer]];
* check for an Old List ring wrap
T ← (eomFragments) - (40C); * at least 2 blocks left
LU ← (eomOldListRead) - T; * or else reset the read pointer
skip[ALU<0], T ← (eomState) AND (newListSizeMask);
*reset the read pointer if there is a wrap.
eomOldListRead ← T, GOTO[eomHiTaskSleep];
GOTO[eomHiTaskSleep];

eomInkwellBlockOld:
T ← (eomOldListRead); * needed to avoid LoadPage gotcha
LoadPage[eomHiTaskPage2];
eomTrapezoid0 ← T, gotoP[eomUpdateInkwellOld];

OnPage[eomHiTaskPage2];
eomUpdateInkwellOld:
T ← (eomTrapezoid0)+(42C), CALL[eomXExtract];
* update Word Pointer

eomSendInkwellRuletteOld:
T ← (eomOldListRead)+(40C), CALL[eomSendRulette];

eomParmBlock0 ← eomParmBlock0, goto[eomContinueTrapezoidOld,R Even];
eomUpdateTrapezoidOld:
T ← (eomOldListRead), goto[eomUpdateTrapezoid];
*
eomParmBlock1 ← T, goto[eomUpdateTrapezoid];
* obtain eomNextY and eomNextHeight

eomContinueTrapezoidOld:
eomParmBlock0 ← (eomParmBlock0)-(10C); * decrement width left by 10
eomParmBlock1 ← (eomParmBlock1)+(1000C), goto[eomInkwellAllDoneOld,alu<0];
* increment X coordinate
T ← (eomOldListRead)+(20C), goto[eomInkwellBandDoneOld,alu<0];
* band is done if carry occurs

*
T ← (eomOldListRead)+(20C);
PStore2[eomParameterTransferBaseA,eomParmBlock0];
T ← (eomOldListRead)+(40C), GOTO[eomInkwellContinueNew];
*inkwell not done, update InkwellRule Information Block

eomInkwellBandDoneOld:
*
eomState ← (eomState) AND Not (workingFromNewList);
T ← (eomOldListRead), goto[eomMoveInkwellBlocks];

*update pointers to reflect 2 Old Blocks consumed, 2 Old Blocks added
eomFinishMoveOld:
eomOldListRead ← T ← (eomOldListRead)+(40C), CALL[eomInkwellBlockGet];
eomOldListWrite ← T ← (eomOldListWrite)+(40C), CALL[eomInkwellBlockPut];
* update the eomOldBlocksFull to account for 2 Old Blocks consumed
eomOldBlocksFull ← (eomOldBlocksFull) - (2C);

* check for an Old List ring wrap (Read Pointer)
T ← (eomFragments) - (40C), CALL[eomCheckOldListRead];

* check for an Old List ring wrap (Write Pointer)
T ← (eomFragments) - (40C), CALL[eomCheckOldListWrite];

*Overflow condition is indicated by read and write pointers being equal.
eomInkwellTestRingOverflow:
T ← eomOldListWrite;
LU ← (eomOldListRead) - T;
T ← (eomFragments) - (40C), goto[eomInkwellNoOverflow,ALU#0];
LoadPage[eomHiTaskPage];
gotoP[eomOldListOverflow];
* test for Old List wrap, at least 2 blocks left

eomInkwellNoOverflow:
T ← (eomOldListRead), goto[eomInkwellContinueNew]; * avoid LoadPage gotcha

eomInkwellAllDoneOld:
eomOldListRead ← T ← (eomOldListRead)+(40C);
* check for an Old List ring wrap
T ← (eomFragments) - (40C); * at least 2 blocks left
LU ← (eomOldListRead) - T; * or else reset the read pointer
skip[ALU<0], T ← (eomState) AND (newListSizeMask);
*reset the read pointer if there is a wrap.
eomOldListRead ← T;

eomOldBlocksFull ← (eomOldBlocksFull) - (2C), goto[eomInkwellNoOverflow];

OnPage[eomHiTaskPage];
eomZPlaneSwitchA:
*ODD BRANCH

*1a.)ABSURD CASE - - CAN NEVER OCCUR
*Processing new list upon entry, AND new list is not empty
*AND there is a z-Plane switch since last block, AND the
*old list is not empty, AND the new list z-Plane value is
*> the old list z-Plane value, AND {the old list z-Plane value is >
*the new list z-Plane value} (FROM eomOldListNotEmptyA).

*1b.)Processing old list upon entry, AND old list is not empty,
*AND old list z-Plane > previous z-Plane (FROM eomOldListNotEmptyA).

*there has been a z-Plane switch, must check the new list.
*First, check whether the new list is empty
DBLGOTO[eomNewListEmptyA,eomNewListNotEmptyA,R<0], LU ← eomNewBlocksFull;

eomNewListEmptyA:
*ODD BRANCH

*1.)Processing old list upon entry, AND old list is not empty,
*AND old list z-Plane > previous z-Plane, AND new list is
*empty (FROM eomZPlaneSwitchA).

*if the new list is empty, we must check to see if the low task is done.
LU ← (eomState) AND (bandDone);
DBLGOTO[eomLoTaskDoneA,eomLoTaskNotDoneA,ALU#0];

eomLoTaskDoneA:
*ODD BRANCH

*1.)Processing old list upon entry, AND old list is not empty,
*AND old list z-Plane > previous z-Plane, AND new list is
*empty, AND the lo task is done (FROM eomNewListEmptyA).

*The low task is done. The transfer must come from the old list. T
*contains the new z-Plane value. Stash this as the new previous value.
GOTO[eomOldListTransfer];

eomLoTaskNotDoneA:
*EVEN BRANCH

*1.)Processing old list upon entry, AND old list is not empty,
*AND old list z-Plane > previous z-Plane, AND new list is
*empty, AND lo task is not done (FROM eomNewListEmptyA).

*the low task is not done. The transfer cannot proceed until there is
*a block in the new list to compare z-Planes with the current old list
*entry.
*
GOTO[eomWaitOnLoTask];
eomState ← (eomState) OR (workingFromNewList), GOTO[eomWaitOnLoTask];

eomNewListNotEmptyA:
*EVEN BRANCH

*1.)Processing old list upon entry, AND old list is not empty,
*AND old list z-Plane > previous z-Plane, AND new list is not
*empty (FROM eomZPlaneSwitchA).

eomState ← (eomState) OR (workingFromNewList), GOTO[eomNewListNotEmpty];

%
********************
SPECIAL TRANSFER BRANCH 4
%
********************

eomCIPBRequest:
*Continue state restore option.
*This option must always come out of the old list, and the z-plane information
*doesn’t exist. In addition, unless something is badly broken, the old list
*will always have enough data to satisfy these requests.
GOTO[eomNoImageWrap], AT[hiTaskWakeupDispatch,10];

%
********************
SPECIAL TRANSFER BRANCH 5
This wakeup indicates that the EOM is ready with a band of data and the IOT can
request it any time.
%
********************

eomBBReadyRequest:
T ← (firstBandReady), CALL[eomHiTaskSwitch], AT[hiTaskWakeupDispatch,12];
GOTO[eomBBBehindRequestPlusTwo];

%
********************
SPECIAL TRANSFER BRANCH 6
This wakeup indicates that the hardware had not processed an end of band character
before the IOT caused a band switch. It means the page is damaged to some degree.
%
********************

eomBBBehindRequest:
T ← (bandBufferBehind), CALL[eomHiTaskSwitch], AT[hiTaskWakeupDispatch,14];
*This NOP is necessary to prevent the call on eomHiTaskModifyWakeupReasons
*from trying to return to hiTaskWakeupDispatch + 16, which is not an
*available location since eomBitMapRequest is already there.
NOP;
eomBBBehindRequestPlusTwo:
CALL[eomHiTaskModifyWakeupReasons];
GOTO[eomHiTaskSleep];

%
********************
SPECIAL TRANSFER BRANCH 7
This wakeup requests a transfer of bitmap data from the EOM back to processor
main memory.
%
********************

eomBitMapRequest:
*it is assumed that there will never be any input data transfers during
*a bitmap output sequence. This means that the same quadblock of ring
*information can be used for transfers going out.
*If it is not the first transfer, do not initialize the stuff.
*Set the branch flag to indicate that the bitmap transfer
*structure has been initialized.
LU ← (eomState) AND (firstParametricReturnBlock), AT[hiTaskWakeupDispatch,16];
GOTO[eomNotFirstBitmap, ALU#0], eomState ← (eomState) OR
(firstParametricReturnBlock);
*Fetch the output buffer information.
PFETCH4[eomCsbBaseLo,eomDataTransferBaseA,outputBufferOffset];
CALL[eomHiTaskSwitch];
*Compute the total number of blocks which will be transferred.
*Get the number of blocks of data per band.
PFETCH1[eomParameterTransferBaseA,eomParmBlock0,parametricBlockCount];

eomNotFirstBitmap:
*NOTE-- IN THIS MODE, eomRingChar CONTAINS THE TRANSFER OFFSET
eomRingChar ← T ← (eomRingChar) + (20C), CALL[eomHiTaskSwitch];
*Check for a ring wrap
LU ← (LSH[eomTerminalBlockAddress,4]) - T;
GOTO[eomNoOutputRingWrap,ALU#0];
*Ring has wrapped, reset the transfer offset.
eomRingChar ← T ← ZERO;

eomNoOutputRingWrap:
*Decrement the count of blocks of data to be transferred.
GOTO[eomThisIsFinalBlock,R<0], eomParmBlock0 ← (eomParmBlock0) - 1;

eomNoOutputRingWrapA:
IOSTORE16[eomDataTransferBaseA,ADD[hiTaskIOoffset,outputChannelBuffer]],
GOTO[eomHiTaskSleep];
eomThisIsFinalBlock:
*The ring data must be saved away now.
PSTORE4[eomCsbBaseLo,eomDataTransferBaseA,outputBufferOffset], CALL[eomHiTaskSwitch];
*Indicate band all returned in the CSB wakeup reasons.
T ← (bandAllReturned), CALL[eomHiTaskModifyWakeupReasons];
T ← eomRingChar, GOTO[eomNoOutputRingWrapA];


********************

*HIGH TASK SUBROUTINES

********************

% SUBROUTINE eomSelectWakeupReasonSub ********************
This subroutine sets up the proper wakeup reason bit in T for the image currently
being processed, then returns. The value in T should not be used on the first
instruction after restarting, due to possible interlock problems.
%

eomSelectWakeupReasonSub:
*Store the image ring quad block for the current image in the hi Csb. This
*is done following an update of the number of sectors of data available.
PSTORE4[eomCsbBaseLo,eomDataTransferBaseA], DISP[eomSelectWakeupReason];
eomSelectWakeupReason:
T ← (image0SectorEmpty), RETURN, AT[hiTaskWakeupReasonDisp];
T ← (image1SectorEmpty), RETURN, AT[hiTaskWakeupReasonDisp,1];
T ← (image2SectorEmpty), RETURN, AT[hiTaskWakeupReasonDisp,2];
T ← (image3SectorEmpty), RETURN, AT[hiTaskWakeupReasonDisp,3];

% SUBROUTINE eomInitBlocksRemaining ********************
This subroutine initializes the blocks remaining in sector value, and modifies the
sectors full (empty) value in the resident ring quad block.
%

eomInitBlocksRemaining:
*Reset the eomBlocksRemaining value to indicate a full sector.
*The CSB contains the actual number of blocks per sector so we must
*subtract 2 to initialize properly.
T ← (LDF[eomRingChar,blocksPerSectorStartBit,blocksPerSectorFieldWidth]);
eomBlocksRemaining ← T;

eomInitBlocksRemainingA:
*Shift the sectors empty value to the high byte
T ← LSH[eomSectorsFilled,blocksPerSectorFieldWidth];
*merge into the proper word in the quad block.
eomRingChar ← (LDF[eomRingChar,blocksPerSectorStartBit,blocksPerSectorFieldWidth]) OR T,
RETURN;


% SUBROUTINE ExtractBlockPointer ********************
This subroutine extracts the block pointer information from the second word of
the most recently sent parameter block. This result is returned in T.
% ********************

eomExtractBlockPointer:
*Mask out the z-plane information.
T ← (eomBlockPointer) AND NOT (17C), RETURN;


% SUBROUTINE PauseHiTask (also DropDead) ********************
This subroutine pauses the high task. Optionally, by entering at DropDead, the
high task status bit which is normally set to indicate the pause, can be
left in the run position. This will prevent the low task from restarting
the high task.
% ********************

eomPauseHiTask:
*indicate pause in status
eomState ← (eomState) AND NOT (hiTaskRunning);
eomDropDead:
INPUT[eomTemp,hiTaskPause];
LU ← eomTemp, RETURN;


% SUBROUTINE eomHiTaskModifyWakeupReasons ********************
This subroutine gets the current wakeup reasons word from the CSB and OR’s into it the
value in T, then puts it back.
% ********************

eomHiTaskModifyWakeupReasons:
*Fetch the wakeup mask and wakeup reasons into eomTemp and eomTemp1
*respectively.
PFETCH2[eomCsbBaseLo,eomTemp,wakeupMaskOffset];
*Use eomImageXferOffset as a temporary and load it with the register
*address in emulator space where the naked notify needs to go.
eomImageXferOffset ← IP[NWW]C;
*Or the new wakeup reason into the current wakeup reasons word.
eomTemp1 ← (eomTemp1) OR T;
*Save the current stack pointer in T. This is actually the ones complement
*of the 8 bit value.
T ← STKP;
eomLoEntryModifyWakeupReasons:
*Put the address of the naked notify register in the stack pointer, and
*simultaneously save the stack pointer value in the eomImageXferOffset
*temporary.
STKP ← eomImageXferOffset, eomImageXferOffset ← T, NOREGILOCKOK;
*Perform the ones complement of the low byte to get the real stack pointer
*back.
eomImageXferOffset ← (eomImageXferOffset) XOR (377C);
*Save the modified wakeup reason back into the CSB.
PSTORE1[eomCsbBaseLo,eomTemp1,wakeupReasonOffset];
*Put the naked notify mask into T.
T ← eomTemp;
*Set up the pointer to the wakeups waiting bit.
eomTemp1 ← IP[RSImage]C;
*OR it into the notify register in the emulator space.
STACK ← (STACK) OR T;
STKP ← (eomTemp1), SKIP[ALU=0];
T ← STACK ← (STACK) OR (IntPendingBit), GOTO[.+2];
T ← STACK;
*Restore the stack pointer to its original value and return.
STKP ← eomImageXferOffset;
RS232 ← T, RETURN;

% SUBROUTINE eomHiTaskSwitch ********************
This subroutine is used for tasking.
% ********************

eomHiTaskSwitch:
NOP, RETURN;

OnPage[eomHiTaskPage2];

% SUBROUTINE eomCheckOldListRead ********************
This subroutine
% ********************

* at least 2 blocks left
eomCheckOldListRead:
LU ← (eomOldListRead) - T; * or else reset the read pointer
skip[ALU<0], T ← (eomState) AND (newListSizeMask);
*reset the read pointer if there is a wrap.
eomOldListRead ← T;
UseCTask, goto[eomHiTaskSwitchP];

% SUBROUTINE eomCheckOldListWrite ********************
This subroutine
% ********************

* at least 2 blocks left
eomCheckOldListWrite:
LU ← (eomOldListWrite) - T;
skip[ALU<0], T ← (eomState) AND (newListSizeMask);
*Ring has wrapped, fix the write pointer
eomOldListWrite ← T; * reset the write pointer
UseCTask, goto[eomHiTaskSwitchP];

% SUBROUTINE eomXExtract ********************
This subroutine
% ********************

eomXExtract:
PFetch1[eomParameterTransferBaseA,eomTemp1];
T ← LDF[eomParmBlock1,1,6];* extract the X coordinate
eomTemp ← T;
T ← (eomTrapezoid0)+(46C);
PStore1[eomParameterTransferBaseA,eomTemp];* write the X coordinate
T ← (eomTemp1)AND Not(170000C); * zero out Word Pointer
eomTemp ← (eomTemp) AND (17C);* extract Word Pointer
eomTemp ← (Lsh[eomTemp,14]) OR (T);
T ← (eomTrapezoid0)+(42C);

% SUBROUTINE eomWriteWordPointer ********************
This subroutine
% ********************

eomWriteWordPointer:
PStore1[eomParameterTransferBaseA,eomTemp];
eomInterlockReturn:
eomTemp←eomTemp, Return;

% SUBROUTINE eomSaveYorH ********************
**** SUBROUTINE eomUpdateBlock ********************
This subroutine
% ********************

eomSaveYorH:
eomUpdateBlock:
PStore1[eomParameterTransferBaseA,eomParmBlock0], goto[eomParmBlockReturn];

% SUBROUTINE eomInkwellBlockGet ********************
This subroutine
% ********************

eomInkwellBlockGet:
PFetch4[eomParameterTransferBaseA,eomParmBlock0], goto[eomParmBlockReturn];

% SUBROUTINE eomInkwellBlockPut ********************
This subroutine
% ********************

eomInkwellBlockPut:
PStore4[eomParameterTransferBaseA,eomParmBlock0];
UseCTask, goto[eomParmBlockReturn];

* SUBROUTINE eomSendRulette ********************
*
This subroutine
* ********************

eomSendRulette:
IOFETCH16[eomParameterTransferBaseA,ADD[hiTaskIOoffset,inputParameterBuffer]];
IOStrobe, goto[eomHiTaskSwitchP];

* SUBROUTINE eomGetNextYorH ********************
*
This subroutine
* ********************

eomGetNextYorH:
PFETCH1[eomParameterTransferBaseA,eomParmBlock0];
eomParmBlockReturn:
eomParmBlock0←eomParmBlock0, Return;

* SUBROUTINE eomGetNextYandH ********************
*
This subroutine
* ********************

eomGetNextYandH:
PFETCH2[eomParameterTransferBaseA,eomTemp], goto[eomInterlockReturn];

* SUBROUTINE eomUpdateYorHeight ********************
*
This subroutine
* ********************

eomUpdateYorHeight:
PFETCH4[eomParameterTransferBaseA,eomTrapezoid0];
* obtain update parameters for Y
T ← eomParmBlock0;
T ← (eomTrapezoid3) + T;
eomParmBlock0 ← T; * Y←Y+Y.wholeAdd

T ← (eomTrapezoid1);
T ← (eomTrapezoid2) + T;
eomTrapezoid1 ← T; * Y.numerator←Y.numerator+Y.numAdd

T ← (eomTrapezoid0) - T; * denominator-Y.numerator
goto[eomUpdateYRtn,alu>=0];
eomTrapezoid1 ← (ZERO) - T;
eomParmBlock0 ← (eomParmBlock0) + 1; * if cumulative fractional parts exceed one, then increase NextY by one

* SUBROUTINE eomHiTaskSwitchP ********************
*
This subroutine is used for tasking.
* ********************

eomUpdateYRtn:
eomHiTaskSwitchP:
Return;

* SUBROUTINE eomUpdateFixH ********************
*
This subroutine
* ********************

eomUpdateFixH:
PFetch1[eomParameterTransferBaseA,eomTrapezoid0];

T ← (34000C);
DISPATCH[eomParmBlock0,16,2];
eomTemp1 ← (ZERO) OR NOT T, DISP[eomSetUpRuleMask0A];
eomSetUpRuleMask0A:
T ← (1C), AT[ruleMaskDispatchA];
eomTrapezoid1 ← (200C), GOTO[eomMergeHeightA];
eomSetUpRuleMask1A:
eomTrapezoid1 ← (360C), GOTO[eomMergeHeightSetTA], AT[ruleMaskDispatchA,1];
eomSetUpRuleMask2A:
eomTrapezoid1 ← (260C), GOTO[eomMergeHeightSetTA], AT[ruleMaskDispatchA,2];
eomSetUpRuleMask3A:
eomTrapezoid1 ← (220C), GOTO[eomMergeHeightSetTA], AT[ruleMaskDispatchA,3];
eomMergeHeightSetTA:
T ← (0C);
eomMergeHeightA:
T ← (LDF[eomParmBlock0,3,13]) - T;
T ← (eomTemp1) AND NOT T;
eomTrapezoid0 ← LDF[eomTrapezoid0,14,2];
T ← (Lsh[eomTrapezoid0,14])OR(T);
eomParmBlock0 ← T, Return;

* SUBROUTINE eomSaveUpdateParms ********************
*
This subroutine
* ********************

eomSaveUpdateParms:
PStore4[eomParameterTransferBaseA,eomTrapezoid0], return;

* ROUTINE eomUpdateTrapezoid ********************
*
This subroutine
* ********************

eomUpdateTrapezoid:
eomParmBlock1 ← T;
T ← (eomParmBlock1) + (22C), CALL[eomGetNextYorH];
* obtain eomNextY

* update next Y
T ← (eomParmBlock1)+(24C), CALL[eomUpdateYorHeight];
T ← (eomParmBlock1)+(22C), CALL[eomSaveYorH];
* save eomNextY in Information Block
T ← (eomParmBlock1)+(24C), CALL[eomSaveUpdateParms];
* save Y parameters
T ← (eomParmBlock1)+(47C), CALL[eomUpdateBlock];
* update Y coordinate in Parameter Block

T ← (eomParmBlock1) + (23C), CALL[eomGetNextYorH];
* obtain eomNextHeight

* update next Height
T ← (eomParmBlock1) + (30C), CALL[eomUpdateYorHeight];
T ← (eomParmBlock1)+(23C), CALL[eomSaveYorH];
* save eomNextHeight in Information Block
T ← (eomParmBlock1)+(30C), CALL[eomSaveUpdateParms];
* save Height parameters
T ← (eomParmBlock1)+(22C), CALL[eomUpdateFixH];
T ← (eomParmBlock1)+(43C), CALL[eomUpdateBlock];
* update Height in Parameter Block

lu ← (eomState)AND(workingFromNewList);
T ← (eomNewListRead)+(20C), Goto[eomContinueTrapezoidNew1,alu#0];
eomContinueTrapezoidOld1:
T ← (eomOldListRead)+(20C);
PFetch2[eomParameterTransferBaseA,eomParmBlock0], goto[eomContinueTrapezoidOld];
eomContinueTrapezoidNew1:
PFetch2[eomParameterTransferBaseA,eomParmBlock0], goto[eomContinueTrapezoidNew];

* ROUTINE eomMoveInkwellBlocks ********************
*
This subroutine
* ********************

eomMoveInkwellBlocks:
eomTrapezoid0 ← T;
T ← (eomTrapezoid0) + (22C), CALL[eomGetNextYandH];
eomParmBlock1 ← (eomParmBlock1)AND Not(177000C); * reset overflow flag and X coordinate
*write the Inkwell Information Block into Old List
T ← (eomOldListWrite)+(20C), CALL[eomInkwellBlockPut];
*move the Rulette skeleton to Old List
T ← (eomTrapezoid0)+(24C), CALL[eomInkwellBlockGet];
T ← (eomOldListWrite)+(24C), CALL[eomInkwellBlockPut];
T ← (eomTrapezoid0)+(30C), CALL[eomInkwellBlockGet];
T ← (eomOldListWrite)+(30C), CALL[eomInkwellBlockPut];
T ← (eomTrapezoid0)+(44C), CALL[eomInkwellBlockGet];
T ← (eomOldListWrite)+(44C), CALL[eomInkwellBlockPut];
T ← (eomTrapezoid0)+(50C), CALL[eomInkwellBlockGet];
T ← (eomOldListWrite)+(50C), CALL[eomInkwellBlockPut];
*Bump the eomOldBlocksReturned value to account for 2 more Blocks.
(eomOldBlocksReturned) ← (eomOldBlocksReturned)+(2C);
lu ← (eomState)AND(workingFromNewList);
Goto[eomFinishMoveNew,alu#0];
goto[eomFinishMoveOld];

END;