:IF[WithEIM]; **************************************

  TITLE[EIM];

%
Ed Fiala 23 April 1982, bum 2 mi at eimInt.
Tom Rich  April 23, 1982  merge EIMDefs and EIM into single module.
Tom Rich  April 22, 1982  make conditional on WithEIM.  merge in EIMInit, move to first overlay.
Tom Rich  March 25, 1982  pNWW => IP[NWW]C, 342c => Stack&-1.
rej April 27, 1981  2:36 PM add missing CR in eomWrap
rej March 26, 1981  9:48 AM fix CSBquad4Offset to proper value	
rej March 13, 1981  4:42 PM  notify at end of band in eomWrapMode
rej March 4, 1981  5:05 PM change field sizes for input buffer parms
rej March 4, 1981  4:48 PM  new EOMwrap
rej November 17, 1980  4:52 PM  output gotcha 15, enableIO
rej June 30, 1980  2:53 PM  mod for amargosa
rej May 8, 1980  10:10 PM  fix process notify
rej January 14, 1980  1:28 PM:dolang vers 5.0	
rej November 19, 1979  12:45 PM  process notify, temps shared with eom
rej June 7, 1979  9:04 AM  eop fix
rej June 14, 1979  4:28 PM  eop overrides parity/overflow
rej June 18, 1979  1:17 PM  init  changed for 8G
rej July 3, 1979  2:47 PM master enable, io enables,mesa requests,clears added for terminal conditions
rej July 20, 1979  3:05 PM  July 3 changes to 4c code included for 8g  
rej August 9, 1979  9:04 AM  large house clean  
rej August 14, 1979  2:01 PM    
%

SET[eimClearAddr,0];					*output register addresses
SET[eimInputBufAddr,OR[lshift[eimTask,4],1]];
SET[eimEimModeAddr, 2];
SET[eimIITmodeAddr,3];
SET[eimNibPerScanAddr,4];
SET[eimIITcommandAddr,6];
SET[eimMaxNibRegAddr,7];

SET[eimDeviceIDaddr,0];					*input register addresses
SET[eimOutputBufAddr,OR[lshift[eimTask,4],1]];
SET[eimMesaStatAddr,2];
SET[eimEimStatAddr,3];
SET[eimCompressStatAddr,4];
SET[eimEOMnextAddr,6];*INPUT is a command rather than a request for data

SET[eimMesaDispLoc, OR [lshift[eimPage,10] , 20]];		*dispatch locations
SET[eimEimDispLoc, OR [lshift[eimPage,10] , 40]];
SET[eim2to1LastLineLoc,OR [lshift[eimPage,10] , 60]];
SET[eim2to1ModeLoc,OR [lshift[eimPage,10] , 100]];

MC[eimoWUenable, 1];					*IO wakeup enable masks
MC[eimiWUenable,2];
MC[eimioWUenable,3];

MC[eimPTeop,1];						*page terminating reasons
MC[eimPTmesaOverflow, 2];
MC[eimPTeimOverflow, 4];
MC[eimPTparity, 10];

SET[eimCSBpage,377];					*CSB address
SET[eimCSBbank,0];
MC[eimrhCSBbase, LSHIFT[eimTask,4]];
MC[eimlhCSBbase, LSHIFT[eimCSBpage,10]];
MC[eimlhCSBbase1, LSHIFT[eimCSBbank,10]];
MC[eimrhCSBbase1, ADD[eimCSBbank,1]];

SET[eimMesaInterruptMaskOffset, 0];				*CSB offsets
SET[eimMesaLockOffset, 1];
SET[eimNemptyOffset, 2];
SET[eimiBufferParmOffset, 3];

SET[eimPTOffset, 4];
SET[eimEimModeOffset, 5];
SET[eimDataBaseOffset, 6];
SET[eimDataBaseOffset1, 7];

SET[eimOutputBaseOffset, 10];
SET[eimoBlockCntrOffset, 11];
SET[eimoBufferSelectOffset, 12];

SET[eimCSBquad4Offset, 14];

SET[eimSWmaxWordsOffset,0];		*pageDescriptor offsets
SET[eimNibblesPerLineOffset,1];
SET[eimLinesPerPageOffset,2];
SET[eimLinesPerBandOffset,3];

SET[eimPBKtablePtrOffset,4];
SET[eimSWSoffset,5];
SET[eimRPTcountOffset,6];

MC[eimInputOffset,400];			*begin of input data buffer

MC[eimLastLineInBand,177777];		*2to1 constants
MC[eimRAW,1];
MC[eimNOR,1];
MC[eimSWTableOffset,20];

********************************************************************************
* eim task registers
********************************************************************************
SETTASK[eimTask];
SET[eimRunO,LSHIFT[eimTask,4]];
SET[eimRbase, AND [60, eimRunO]];

RV[eimCSBbase, ADD[eimRbase,0]];			*long pointers
RV[eimCSBbase1, ADD[eimRbase,1]];
RV[eimDataBase, ADD[eimRbase,2]];
RV[eimDataBase1, ADD[eimRbase,3]];

RV[eimiBuffPtr, ADD[eimRbase,4]];
RV[eimiBlockCntr, ADD[eimRbase,5]];
RV[eimiSectorCntr, ADD[eimRbase,6]];
RV[eimSWentryNum, ADD[eimRbase,7]];

RV[eimSWmaxWords, ADD[eimRbase,10]];
RV[eimNibblesPerLine, ADD[eimRbase,11]];
RV[eimLineInPage, ADD[eimRbase,12]];
RV[eimLineInBand, ADD[eimRbase,13]];

SET[eimtemp1, ADD[eimRbase, 14]];	
SET[eimtemp2, ADD[eimRbase, 15]];
SET[eimtemp3, ADD[eimRbase, 16]];
SET[eimtemp4, ADD[eimRbase, 17]];

RV[eimTempReg1, eimtemp1];			*temporary  registers
RV[eimTempReg2, eimtemp2];
RV[eimTempReg3, eimtemp3];
RV[eimTempReg4, eimtemp4];

RV[eimNibblesInPage, ADD[eimRbase,30]];	*registers in bank shared with eom
RV[eimNibblesInPage1, ADD[eimRbase,31]];
RV[eimoBuffPtr, ADD[eimRbase,32]];	
RV[eimoBlockCntr, ADD[eimRbase,33]];

********************************************************************************
*temporary  registers 
********************************************************************************
RV[eimDummyReg, ADD[eimRbase,37]];	*temp used by both EIM and EOM
RV[eimTempReg5, ADD[eimRbase,36]];	*temp used by both EIM and EOM
RV[eimTempReg6, ADD[eimRbase,37]];	*temp used by both EIM and EOM

RV[eimClear, eimtemp1];
RV[eimMesaInterruptMask, eimtemp1];
RV[eimOutputBase, eimtemp1];
RV[eimOldSWentry, eimtemp1];

RV[eimEimMode, eimtemp2];
RV[eimMesaLock, eimtemp2];
RV[eimMesaStatus, eimtemp2];
RV[eimNewSWentry, eimtemp2];

RV[eimNsectorsEmpty, eimtemp3];
RV[eimMNR, eimtemp3];
RV[eimRPTcount, eimtemp3];
RV[eimPBKtablePtr, eimtemp3];
RV[eimPT, eimtemp3];

RV[eimEimStatus, eimtemp4];
RV[eimiBufferParm, eimtemp4];
RV[eimoBufferSelect, eimtemp4];
RV[eimCompressorStatus, eimtemp4];

********************************************************************************
* initialization code
********************************************************************************

eimInit:
	eimCSBbase←eimrhCSBbase, At[eimInitLoc];
	eimCSBbase←(eimCSBbase) OR (eimlhCSBbase);
	eimCSBbase1←eimrhCSBbase1;
	eimCSBbase1←(eimCSBbase1) OR (eimlhCSBbase1);

	LOADPAGE[eimPage] ;
	OUTPUT[eimClear,eimClearAddr], GotoP[eimSleep];

ONPAGE[eimPage];

eimNOPreturn:
	nop, RETURN;
eimSleep:
	IOSTROBE, CALL[eimNOPreturn];
eimFirstInstruction:
	GOTO[eimRun];
********************************************************************************
	*why was I awakened?
********************************************************************************
eimRun:
	DBLGOTO[eimNonInputWU, eimInputWU, IOATTEN];
eimNonInputWU:
	INPUT[eimEimStatus,eimEimStatAddr];		*read eimstatus
	DISPATCH[eimEimStatus,15,3], LU←eimEimStatus;
	DISP[eimEimDisp];
********************************************************************************
	*subroutines
********************************************************************************
eimFetchIntMask:
	RETURN, PFETCH1[eimCSBbase, eimMesaInterruptMask, eimMesaInterruptMaskoffset];
eimFetchEIMmode:
	RETURN, PFETCH1[eimCSBbase, eimEimMode, eimEimModeOffset];

eimTerminalCondition:
	CALL[eimFetchIntMask];
	OUTPUT[eimClear,eimClearAddr];
	PSTORE1[eimCSBbase, eimPT, eimPToffset];
eimNotify:
	CALL[eimInt],IOSTROBE;
	GOTO[eimRun];
eimInt:
	eimTempReg5 ← IP[NWW]C;
	T ← (SStkP&NStkP) xor (377C);
	StkP ← eimTempReg5, eimTempReg5 ← T, NoRegILockOK;
	T ← eimMesaInterruptMask;
	Stack ← (Stack) or  T, Skip[ALU#0];
	  StkP ← eimTempReg5, Return;
	Stack&-1;		*Point StkP at RSImage
	T ← Stack ← (Stack) or (IntPendingBit);
	LU ← StkP ← eimTempReg5, RS232 ← T, RETURN;

********************************************************************************
	* eimInputWU
********************************************************************************
********************************************************************************
	* transfer loop
********************************************************************************

eimInputWU:
	GOTO[eimiNewSector,R<0], eimiBlockCntr←(eimiBlockCntr)-(1c);
eimiBlockTransfer:
	t←eimiBuffPtr;
	TASK,IOSTROBE,IOSTORE20[eimDataBase, eimOutputBufAddr];
	eimiBuffPtr←(eimiBuffPtr)+(20c);
	GOTO[eimRun];

********************************************************************************
	* ring management
********************************************************************************
eimiNewSector:
	*fetch nsectorsempty , bufferparm, eimMesaInterruptMask
	CALL[eimNOPreturn], PFETCH4[eimCSBbase, eimMesaInterruptMask,0];
	DBLGOTO[eimiHead, eimiBody, R<0], eimiSectorCntr←(eimiSectorCntr)-(1c);
eimiHead:
	eimiBuffPtr←eimInputOffset;					*set to start of  ring
	t←(LDF[eimiBufferParm,0,7]);		*reset sector count  (new field size)
	eimiSectorCntr←t;
eimiBody:
	t←(LDF[eimiBufferParm,7,11])-1;	*minus one since a block will be delivered (new field size)
	eimNsectorsEmpty←(eimNsectorsEmpty)-(1c);
	GOTO[eimiBufferFull, ALU=0], eimiBlockCntr←t;
	     CALL[eimNOPreturn];	*task before doing interrupt, ugh!
	     CALL[eimInt], PSTORE4[eimCSBbase, eimMesaInterruptMask,0];
	     GOTO[eimiBlockTransfer];

eimiBufferFull:						*Mesa fell behind
	GOTO[eimTerminalCondition], eimPT←eimPTmesaOverflow;*Nsectors will not be updated


********************************************************************************
	*parity wakeup and Overflow wakeup
********************************************************************************
eimEimDisp:
eimParity:					AT[eimEimDispLoc,1],
	GOTO[eimTerminalCondition], eimPT←eimPTparity;
eimOverflow:				AT[eimEimDispLoc,2],
	GOTO[eimTerminalCondition], eimPT←eimPTeimOverflow;

********************************************************************************
*2to1 wakeup	
********************************************************************************
********************************************************************************
	*end of page?
	*two extra RAW lines after last line?
	*normal 2to1 (including dummy at the beginning)?
********************************************************************************
eim2to1:						AT[eimEimDispLoc,3],
	SKIP[R<0], eimSWentryNum←(eimSWentryNum)-1;
	     PFETCH1[eimDataBase,eimSWentryNum,eimSWSoffset]; 
	CALL[eimNOPreturn], INPUT[eimCompressorStatus, eimCompressStatAddr];
	GOTO[eim2to1notForcedRAW,R>=0], eimLineInPage←(eimLineInPage)-1;
	      t←(eimLineInPage)+(3c);
	      GOTO[eim2to1setRAW, ALU>=0], t←zero;	*force two RAW lines after last line
********************************************************************************
*End Of Page
********************************************************************************
eimEndOfPage:
	GOTO[eimTerminalCondition], eimPT←eimPTeop;
********************************************************************************
*fetch old SW entry
********************************************************************************
eim2to1notForcedRAW:
eim2to1oldEntry:
	t←(eimSWentryNum)+(eimSWTableOffset);
	PFETCH1[eimDataBase,eimOldSWentry]; 

********************************************************************************
*compute new entry:  NOR←compressed nibbles, RAW←nibbles/line, RPT←2
********************************************************************************
	DISPATCH[eimCompressorStatus,3,2], LU←eimCompressorStatus;
	DISP[eim2to1LastLine], LU←ldf[eimCompressorStatus,0,1];*subtract one nibble?(last lineNOR)

eim2to1LastLine:
eim2to1LastLineNOR:				AT[eim2to1LastLineLoc,0],*NOR
	GOTO[eim2to1NewEntry, ALU=0],t←ldf[eimCompressorStatus,5,13];*compressed nibbles
	     GOTO[eim2to1NewEntry], t←(ldf[eimCompressorStatus,5,13] )-1;*subtract one nibble
eim2to1LastLineRAW:				AT[eim2to1LastLineLoc,1],*RAW
	GOTO[eim2to1NewEntry], t←eimNibblesPerLine;
eim2to1LastLineRPT:					AT[eim2to1LastLineLoc,2],*RPT
	TASK, t←(2c);
	PFETCH1[eimDataBase,eimRPTcount,eimRPTcountOffset];
	eimRPTcount←(eimRPTcount)+1;
	PSTORE1[eimDataBase,eimRPTcount,eimRPTcountOffset];

eim2to1NewEntry:
	eimNewSWentry←t;
	CALL[eimNOPreturn], eimNewSWentry←rsh[eimNewSWentry,2];*nibbles to words
	eimNibblesInPage←(eimNibblesInPage)+t;
	GOTO[eim2to1Calculation, NOCARRY], eimNewSWentry←rsh[eimNewSWentry,2];*nibbles to words
	     GOTO[eim2to1Calculation], eimNibblesInPage1←(eimNibblesInPage1)+1;*double precision add

********************************************************************************
*2to1 wakeup	2to1 calculations	
********************************************************************************
*store eimNewSWentry, select  mode and calculate new  SWmaxWords
********************************************************************************
eim2to1Mode:
	DISP[eim2to1ModeDisp], PSTORE1[eimDataBase,eimNewSWentry];
eim2to1ModeDisp:
eim2to1NormalModePos:				AT[eim2to1ModeLoc,0],
	t←eimOldSWentry;
eim2to1NormalModePos1:
	eimSWmaxWords←(eimSWmaxWords)+t;
	t←eimNewSWentry;
	RETURN, eimSWmaxWords←(eimSWmaxWords)-t;
eim2to1NoRPTs:					AT[eim2to1ModeLoc,1],
	RETURN;
eim2to1AltRPTs:					AT[eim2to1ModeLoc,2],
	RETURN;
eim2to1NormalModeNeg:				AT[eim2to1ModeLoc,3],
	GOTO[eim2to1NormalModePos1], t←eimOldSWentry;
********************************************************************************
	*determine max nibbles per scan for next line
********************************************************************************
eim2to1Calculation:
	t←(eimSWentryNum)+(eimSWTableOffset);
	CALL[eim2to1Mode], DISPATCH[eimSWmaxWords,0,2];
eim2to1MaxNibbles:
	GOTO[.+2,r>=0], t←eimSWmaxWords;
	     GOTO[eim2to1RAWRPT], t←2c;	*eimSWmaxWords was negative
	LU←(RSH[eimNibblesPerLine,2])-t-1;	*is eimSWmaxWords less than a RAW line?
	GOTO[eim2to1RAWRPT, ALU>=0], t←LSH[eimSWmaxWords,2];
	     GOTO[eim2to1setRAW], t←eimNibblesPerLine;	
********************************************************************************
	*determine RAW or RPT for next line	RAW=1		RPT=0
********************************************************************************
eim2to1RAWRPT:
	eimMNR←t;
	LU←ldf[eimCompressorStatus,3,1];		*was last  line a repeat?
	GOTO[.+2, ALU=0] ,LU←(eimLineInBand);	*no 
	       GOTO[eim2to1setRAW], t←eimNibblesPerLine;	*yes, set RAW
	GOTO[eim2to1setRPT, ALU>=0];	*next line is not the beginning of a band
	       GOTO[eim2to1setRAW], t←eimNibblesPerLine;	*begin band must be RAW
eim2to1setRPT:
	GOTO[eim2to1PBKNOR], eimMNR←LSH[eimMNR,1] ;
eim2to1setRAW:
 	       eimMNR←t;
	       eimMNR←(LSH[eimMNR,1] )+1;	*shift and  set  RAW bit
********************************************************************************
	*determine PBK or NOR next +1  line	PBK=0		NOR=1
********************************************************************************
eim2to1PBKNOR:
	LU←(eimLineInBand);	*zero is next to last line in band;send PBK on next to last line
	GOTO[eim2to1outputMNR, ALU=0], eimMNR←LSH[eimMNR,1];
	       GOTO[eim2to1outputMNR], eimMNR←(eimMNR) OR (eimNOR);
eim2to1outputMNR:
	eimMNR←LCY[eimMNR,16];
	OUTPUT[eimMNR,eimMaxNibRegAddr];
	CALL[eimNOPreturn];
********************************************************************************
*2to1 wakeup	PBKtable, eom wraparound	
********************************************************************************
eim2to1EomWrap:
	LU←LDF[eimCompressorStatus,1,1];	*are we in EOMwrap mode?
	GOTO[eim2to1updateLineInBand, ALU=0];
	     GOTO[eim2to1EOMstart, R>=0], LU←eimLineInBand;*last line in band?
		  nop;	*placement
		  CALL[eimFetchIntMask];
	                CALL[eimInt], PSTORE4[eimCSBbase,eimSWmaxWords,eimCSBquad4Offset];
		  *last word in quad is lineInBand which is -1; reason for notify; Mesa should reset
	                GOTO[eim2to1updateLineInBand];
eim2to1EOMstart:
	INPUT[eimDummyReg,eimEOMnextAddr];
eim2to1updateLineInBand:
	SKIP[ R<0], eimLineInBand←(eimLineInBand)-1;*last line in band?
	GOTO[eimSleep];
	     NOP;	*make sure write of eimLineInBand occurs before fetch
	     PFETCH1[eimDataBase,eimLineInBand,eimLinesPerBandOffset]; 
	     CALL[eimNOPreturn], PFETCH1[eimDataBase,eimPBKtablePtr,eimPBKtablePtrOffset]; 
	     t←eimPBKtablePtr;
	     TASK, PSTORE2[eimDataBase,eimNibblesInPage];
	     eimPBKtablePtr←(eimPBKtablePtr)+(2c);
	     GOTO[eimSleep], PSTORE1[eimDataBase,eimPBKtablePtr,eimPBKtablePtrOffset]; 

********************************************************************************
*eimMesaRequest
********************************************************************************

eimMesaRequest:			AT[eimEimDispLoc,5],
	PFETCH4[eimCSBbase,eimMesaInterruptMask,0];
	CALL[eimNOPreturn];
	INPUT[eimMesaStatus,eimMesaStatAddr];*overwrites lock from PFETCH;will be reset later
	DISPATCH[eimMesaStatus,15,3], LU←eimMesaStatus;*lock untill it gets here
	DISP[eimMesaDisp];

eimMesaDisp:

********************************************************************************
	*increment NsectorsEmpty
********************************************************************************
eimMesaNsectorsEmpty:	AT[eimMesaDispLoc,0],
	GOTO[eimMesaClearMesaLock], eimNsectorsEmpty←(eimNsectorsEmpty) +(1c);
********************************************************************************
	*disable eim wakeups
********************************************************************************
eimMesaDisableEimWakeups:		AT[eimMesaDispLoc,1],
	GOTO[eimMesaClearMesaLock], OUTPUT[eimClear,eimClearAddr];
********************************************************************************
	*enable IO wakeups
********************************************************************************
eimMesaEnableInput:		AT[eimMesaDispLoc,2],
	GOTO[.+2], t←eimiWUenable;	*enable input wakeup only (IIT source)
eimMesaEnableIO:		AT[eimMesaDispLoc,3],
	t←eimioWUenable;	*enable input and output (DO source)
	CALL[eimFetchEIMmode];
	eimEimMode←(eimEimMode) OR (t);
	OUTPUT[eimEimMode,eimEimModeAddr];
	NOP;	*allow write of eimEIMMode
	GOTO[eimMesaClearMesaLock], PSTORE1[eimCSBbase, eimEimMode, eimEimModeOffset];

********************************************************************************
	*page init
********************************************************************************
eimMesapage init:	AT[eimMesaDispLoc,4],

	PFETCH2[eimCSBbase, eimDataBase, eimDataBaseOffset];
	eimiBuffPtr←eimInputOffset;	*one page from start of DataBase
	t←(LDF[eimiBufferParm,0,7]);		*reset sector count  (new field size)
	eimiSectorCntr←t;
	PFETCH2[eimCSBbase, eimoBuffPtr, eimOutputBaseOffset];
	t←(LDF[eimiBufferParm,7,11]);		*reset block count  (new field size)
  	eimiBlockCntr←t;
	eimNibblesInPage←zero;
	TASK, PFETCH4[eimDataBase, eimSWmaxWords, eimSWmaxWordsOffset];
	eimNibblesInPage1←zero;
	eimLineInBand←(zero)-1;	*last line of imaginary 0th band
	GOTO[eimMesaClearMesaLock], eimSWentryNum←zero;
********************************************************************************
	*start EOM data
********************************************************************************
eimMesaStartEOMdata:		AT[eimMesaDispLoc,5],
	GOTO[eimMesaClearMesaLock], INPUT[eimDummyReg,eimEOMnextAddr];
********************************************************************************
	*clear lock
********************************************************************************
eimMesaClearMesaLock:
	eimMesaLock←0c;
	NOP;	*make sure lock is written
	GOTO[eimSleep], PSTORE4[eimCSBbase, eimMesaInterruptMask,0];  
********************************************************************************
	*eimOutWU
********************************************************************************
********************************************************************************
	* transfer loop
********************************************************************************

eimOutWU:			AT[eimEimDispLoc,6],
	GOTO[eimoSectorEmpty,R<0], eimoBlockCntr←(eimoBlockCntr)-(1c);
eimoBlockTransfer:
	t←eimoBuffPtr;
	TASK,IOSTROBE,IOFETCH20[eimDataBase, eimOutputBufAddr];
	eimoBuffPtr←(eimoBuffPtr)+(20c);
	GOTO[eimRun];

********************************************************************************
	* buffer management
********************************************************************************
eimoSectorEmpty:
	CALL[eimFetchEIMmode];
	eimEimMode←(eimEimMode) AND NOT (eimoWUenable);
	OUTPUT[eimEimMode,eimEimModeAddr];		*inhibit wakeups
	PFETCH1[eimCSBbase, eimoBufferSelect, eimoBufferSelectOffset];
	TASK, t←eimoBuffPtr;*save buffPtr, it will be clobbered by PFETCH
	PSTORE1[eimCSBbase, eimEimMode, eimEimModeOffset];

	PFETCH2[eimCSBbase, eimoBuffPtr, eimOutputBaseOffset];*reset BuffPtr
	CALL[eimFetchIntMask];
	eimoBufferSelect←(eimoBufferSelect) XOR (1C);	*flip sectors
	SKIP[ ALU=0],  LU←eimoBuffPtr;*insure that fetch occurs first
	        eimoBuffPtr←t;	*restore  BuffPtr
	GOTO[eimNotify], PSTORE1[eimCSBbase,eimoBufferSelect, eimoBufferSelectOffset];

  END[EIM];

:ELSE; *********************************************

  TITLE[No.EIM.microcode];

:ENDIF; ********************************************