{File name:  IOPMain.mc
 Last Edited by Dennis DEG     :  1-Sep-84 19:36:09: Add copyright notice.
 Last Edited by AEF AEF     :  4-Mar-83 11:26:19: Make uBufMax a two-word quantity to fix gateway freeze problem
 Last Edited by Jim JXF     : May 25, 1982  11:40 AM: Add virtual addressing and page crossing.
 Written by Jim JXF     : March 11, 1982  10:47 AM
}

{ 	Copyright (C) 1982, 1983 by Xerox Corporation.  All rights reserved.}

Set[IOPTask, 5];
Set[IOPOutMode, 2];		{IOP port output mode}
Set[IOPAWMode, 3];		{IOP port alwaysWU mode}

SetTask[IOPTask], StartAddress[IOPIdle];

{This is the module that communicates with the IOP (the 8085). We start in IOPInMode, which means we wake up when the IOP is trying to tell us something. We read 8 bits from the IOP each wakeup. We read:

low address (10:17) 
middle address (2:9) 
high address (0:1) 
count in bytes for write and read, or mask for naked notify.
command is last:
	0 - Do Naked Notify
	1 - Read Memory Block
	2 - Write Memory Block
}
	
IOPIdle:	rIOP ← IOPIData, L7 ← 0F, CANCELBR[$,0F], 		c1;
	rIOP ← rIOP LRot8, {low, xxx}, L7Disp, CALL[IOPNoop],		c2;
{	Noop,								c3;}

	rIOP ← rIOP or IOPIData {Middle address (2:9)}, L7 ← 4, 	c1, at[0F,10,IOPSub]; 
	rIOP ← rIOP LRot8, {High, Low} L7Disp, CALL[IOPNoop],		c2;
{	Noop, 								c3;}

	uIOPVirtLow ← rIOP,  						c1, at[4,10,IOPSub];
	rhIOP ← rIOP ← IOPIData {high address (0:1)},		       	c2;
	L7 ← 5, uIOPVirtHigh ← rIOP,					c3;

{Next comes the parameter.  It is count-2 for write and read and mask for naked notify.}

	rTmp ← IOPIData, CALL[rTmpLRot8], 				c1;
{	rTmp ← rTmp LRot8, {Left justify}				c2;}
{	Noop,								c3;}

	rTmp ← rTmp or IOPIData, L7 ← 6, CALL[rTmpLRot8],  		c1, at[5,10,IOPSub];
{	rTmp ← rTmp LRot8, 						c2;}
{	Noop,								c3;}

	Xbus ← IOPIData, XDisp,						c1, at[6,10,IOPSub];
	rIOP ← uIOPVirtLow, L7 ← 0 {Put command in L7}, DISP2[IOPCmd],	c2;

{**************************************************************************
	Do Naked Notify.
	IOP Control is in InMode
	mask is in rTmp.
	L7 = 0
	Set control to Always Wakeup so we can do another click.
**************************************************************************}

	IOPCtl ← IOPAWMode,						c3, at[0,4,IOPCmd];

{OR into wakeup U-register.  This must be done in one click.}
	rIOP ← uWP, MesaIntRq,						c1;
	rTmp ← rTmp or rIOP, IOPCtl ← IOPInMode,			c2;
	uWP ← rTmp, GOTO [IOPIdle],					c3;

{**************************************************************************
	Read or Write memory block.
	IOP Control is in InMode
	Bytes come in the order high byte, low byte
	uIOPVirtHigh and rhIOP contain the virtual address high
	uIOPVirtLow and rIOP contain the virtual address low
	rTmp = count in bytes. 
	L7 = 1 (read) or 2 (Write)
	First, compute the last virtual address
**************************************************************************}

{Come here if Write Command. For a Write Command, the last address must point to the last word transferred, so we subtract 2 from the byte count.}
	rTmp ← rTmp-2, GOTO[CalculateLast],				c3, at[2,4,IOPCmd];

{Come here if Read Command. For a read command, the last address must point to one word past the last word actually read. We will wakeup at CalculateLast since the data register is empty, and the port is in OutMode.  The IOP task will continue to wakeup until a byte is output to the port.}
	 IOPCtl ← IOPOutMode, GOTO[CalculateLast],			c3, at[1,4,IOPCmd];

{For Write, we will wakeup here since the data register is empty, and the port is in OutMode.  The IOP task will continue to wakeup until a byte is output to the port. }

CalculateLast:
	rTmp ← RShift1 rTmp,						c1;
	rTmp ← rTmp + rIOP, CarryBr,					c2;
TestCarry:
	uData ← rTmp, rIOP ← rTmp, BRANCH[MapLast, $],			c3;

	rIOP ← rhIOP+1, LOOPHOLE[byteTiming],				c1;
	rhIOP ← rIOP LRot0, GOTO[TestCarry], 				c2;

{**************************************************************************

	Map last virtual address.
	On entry, last virtual address low is in uData, rIOP,  and rTmp
	last virtual address high is in rhIOP
	Return with last real address low in uBufMaxLow and uBufMaxHigh
**************************************************************************}

MapLast:
	Map ← [rhIOP, rIOP], 						c1;
	rTmp ← ~0FF,							c2;
	rhIOP ← MD, rIOP ← MD and rTmp {FF00},				c3;

{Now we have the real page number in rhIOP and rIOP[0..7].
Set up rIOP to contain the low 16 bits of the last real address.}

	rTmp ← uData {Saved virtual address low},			c1;
	rTmp ← rTmp and 0FF,  						c2;
	uBufMaxLow ← rIOP or rTmp,				  	c3; 

	rIOP ← rhIOP,							c1;
	uBufMaxHigh ← rIOP,						c2;
	Noop, {0 disp} CALL[IOPNoop],					c3;

{**************************************************************************
	Set the virtual address to point to word 0 of the next page.
	uIOPVirtHigh contains the virtual address high
	uIOPVirtLow contains the virtual address low
	Save rTmp in uData for ReadBlock
**************************************************************************}

MapNextPage:
	uData ← rTmp,							c1, at[2,10,IOPSub];
	rIOP ← 0FF,							c2;
	rTmp ← uIOPVirtLow,						c3;

{Calculate next virtual address as old virtual address + 256.  Then set rIOP to point to the first word of the page.}
	rIOP ← rIOP {0FF} + rTmp {old virtual address} + 1, CarryBr,	c1;
	rIOP ← rIOP and ~0FF, BRANCH[MapVirt3, $],			c2;
	rTmp ← uIOPVirtHigh,						c3;

	rTmp ← rTmp + 1,						c1;
	uIOPVirtHigh ← rTmp, GOTO[MapVirt3],				c2;


{**************************************************************************

	Map virtual address.
	On entry, virtual address is in uIOPVirtHigh and uIOPVirtLow
	Return with real address in rhIOP,,rIOP
**************************************************************************}

MapVirtual:
	rIOP ← uIOPVirtLow,						c2, at[0,10,IOPSub];
MapVirt3:
	rhIOP ← uIOPVirtHigh,						c3;

{Save virtual address. If we came here from MapNextPage, then rIOP points to the first word of a new page; otherwise rIOP map point to any word in the page.}
	Map ← [rhIOP, rIOP], uIOPVirtLow ← rIOP, 			c1;
	rTmp ← ~0FF,							c2;
	rhIOP ← MD, rIOP ← MD and rTmp {FF00},				c3;

{Now we have the real page number in rhIOP and rIOP[0..7].
Set up rIOP to contain the low 16 bits of the real address.}

	rTmp ← uIOPVirtLow,						c1;
	rTmp ← rTmp and 0FF, L7Disp, 					c2;
	rIOP ← rIOP or rTmp, DISP2[IOPMapRet], 				c3; 

{**************************************************************************
	Begin WriteBlock loop.
	On entry, real address is in rhIOP,,rIOP
	L7 = 2
**************************************************************************}

WriteBlock:
	[] ← rIOP xor uBufMaxLow, ZeroBr,				c1, at[2,4,IOPMapRet];
	rTmp ← IOPIData {00, High},BRANCH[$, CheckForLastByte], 	c2;
	rTmp ← rTmp LRot8 {High, 00},					c3;
	
WriteBlockLoop:
	MAR← [rhIOP, rIOP], rIOP←rIOP+1,				c1; 
	rTmp ← MDR ← rTmp or IOPIData, BRANCH [$, WritePageCross, 1],	c2; 
	Noop, GOTO[WriteBlock],						c3;

{This byte is the last byte.  There will be no page crossing, so do the memory reference and then go back to top of the loop}

CheckForLastByte:
	rIOP ← rhIOP,							c3; 
	
	[] ← rIOP xor uBufMaxHigh, ZeroBr,				c1; 
	rIOP ← uBufMaxLow, BRANCH[ContinueLoop, $],			c2; 
	rTmp ← rTmp LRot8,						c3; 

	MAR← [rhIOP, rIOP+0],						c1; 
	MDR ← rTmp or IOPIData, 					c2; 
IOPDone:
	IOPCtl ← IOPInMode, GOTO [IOPIdle],				c3;

{Here the low address bits match those of the last real address (uBufMaxLow), but the high address bits (uBufMaxHigh) don't.  Go back to the loop and continue.}

ContinueLoop:
	rTmp ← rTmp LRot8 {High, 00}, GOTO[WriteBlockLoop],		c3;

{The previous MAR← instruction generated a page cross branch.  The data was not written.  The Data to be written is saved in rTmp, and it was not the last data byte, so we will get at least one more wakeup.  Don't read in new data until we are finished with this data.  rIOP is now pointing to the beginning of this page, not at the beginning of the next page.  We still have to write the last word of this page. }

WritePageCross:
	Noop,								c3; 

{No page cross is possible on next instruction. Store the saved byte in the last location of the page. }
	MAR ← [rhIOP, rIOP + 0FF],					c1; 
	MDR ← rTmp, L7Disp, CANCELBR[IOPNoop,0],			c2; 
{	Noop, GOTO[MapNextPage],					c3;} 


{**************************************************************************
	Begin ReadBlock loop.
	On entry, real address is in rhIOP,,rIOP
	The last address+1 is in uBufMax
	Data is sent to the port high byte first, then low byte.
	L7 = 1
	Change L7 to 3 so we can have another return point from IOPMapRet.
**************************************************************************}

	MAR ← [rhIOP, rIOP], rIOP ← rIOP + 1, L7 ← 3,			c1, at[1,4,IOPMapRet]; 
	Noop, BRANCH[NoReadCross, ReadCross, 1],			c2; 

SendHighByte:
	IOPOData ← rTmp LRot8 {Output High byte},			c2, at[3,10,IOPSub]; 
	[] ← rIOP xor uBufMaxLow, ZeroBr,				c3;

SendLowByte:
	MAR ← [rhIOP, rIOP], rIOP←rIOP+1, BRANCH[$,CheckForSendLastByte],	c1; 
	IOPOData ← rTmp LRot0, BRANCH[NoReadCross, ReadCross, 1],	c2;
NoReadCross:
	rTmp ← MD, L7Disp, CALL[IOPNoop],				c3;

{	Noop, GOTO[SendHighByte],					c1;}

{This may be the last byte.  The next address might have generated a page crossing, but we don't need to worry about it.  Just cancel it, and then go back to top of the loop.}

CheckForSendLastByte:
	rIOP ← rhIOP, CANCELBR [$, 2],					c2; 
	[] ← rIOP xor uBufMaxHigh, ZeroBr,				c3;
	 
	rIOP ← uBufMaxLow, BRANCH[ContinueSendLoop, $],			c1; 
	IOPOData ← rTmp LRot0, GOTO[IOPDone],				c2; 

ContinueSendLoop:
	Noop,								c2; 
	GOTO[SendLowByte],						c3; 

{The previous MAR← instruction generated a page cross branch.  rIOP is now pointing to the begining of this page, instead at the beginning of the next page.  Catch the last memory data, fix up rIOP to the correct address and continue. }

ReadCross:
	rTmp ←  MD, CALL [MapNextPage],					c3; 

{Return here from MapNextPage. rTmp, which contains the next word, was saved in uData.}

	rTmp ← uData, GOTO[SendHighByte],				c1, at[3,4,IOPMapRet];

rTmpLRot8:
	rTmp ← rTmp LRot8, L7Disp, 					c2;
IOPNoop:
	Noop, RET[IOPSub]						c*;