{File name:  IOP.mc
 Last Edited by Dennis Grundler:  1-Sep-84 22:09:539: add copyright notice.
 Last Edited by Jim Frandeen: August 20, 1981  4:12 PM: changes for new assembler
 Last Edited by Sandman: November 7, 1980  3:29 PM
 Last Edited by Roy Ogus: July 8, 1980  12:04 AM
 Description: Microcode to implement the IOP-CPport functions.
 Author: Roy Ogus}

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

{LOG:
	-  Created (March 28, 1980  4:08 PM)
	-  2 click inner loop (from 3 click) (April 3, 1980  10:30 PM)
	-  reversed sequence of bytes (April 7, 1980  4:07 PM)
	-  addition of Naked Notify function (July 7, 1980  6:09 PM)
	-  common code; swapped cmds (November 7, 1980  3:16 PM)
}

{NOTES:
-  Word counter is used.  Must be an even number of bytes.
-  BYTE ORDER:  normal byte order is low byte, high byte.  
	Read/Write Swapped command data transfer order is high byte, low byte.
-  Memory addresses are real.  Buffers are in contiguous real pages.
-  The inner loop uses a maximum buffer address as terminator.
-  Buffers should not cross 64K boundaries.}

{Register usage:
	rIAd	-	Memory address
	RHrIAd	-	High memory address
	rTmp	-	Temp accumulator
	uData	-	Saved data
	uBufMax	-	Maximum buffer address}


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

{  loaded into L7
   COMMANDS:
	0 - Write Memory Block
	1 - Read Memory Block
	2 - Do Naked Notify
	3 - (unimplemented)
	4 - (unimplemented)
	5 - Write Swapped Block
	6 - Read Swapped Block}

{IOP  Task code}

	SetTask[IOPTask], StartAddress[IOPIdle];

{Top of the IOP code.  The code is started at this point for a new command.
  IOP Control should be set to InMode.}

{Read in first byte from the IOP port and interpret it as a command}
IOPIdle:	CANCELBR[$,0F],	c1; {Dispatch}
	Xbus ← IOPIData, XDisp,	c2;
	L7 ← 0, DISP3[IOPCmd],	c3;


{**************************************************************************}

{COMMAND 0:  Write memory block.
     Format of transfer (bytes):
	command (2 bits), low addr (10:17), mid. addr (2:9), high addr (0:1), 
	 low count-1 (8:15), high count-1 (0:7),
	 low data, high data, .....

    IOP Control is in InMode}

{return from IOPSub}
	rTmp ← rTmp + rIAd,	c3, at[0,10,IOPRet];


{Read the first byte from the port (low byte).}
WFirst:	uBufMax ← rTmp,	c1; {Store in uBufMax}
	rTmp ← IOPIData, GOTO [WLo3],	c2; {Read in low byte}

{Write Memory Block Inner Loop}

{High byte.  Or with low byte, flip bytes.  Test for end of buffer.}
WHiByte:	[] ← rIAd xor uBufMax, ZeroBr,	c1; {Check for max. address}
	rTmp ← rTmp or IOPIData, BRANCH[$,WLo3Last],	c2; {OR in high byte: Low, High}
	rTmp ← rTmp LRot8,	c3; {Flip bytes: High, Low} 

{Store the word, read in the next low byte.  Check for page crossing as well.}
WLo:	MAR← [RHrIAd, rIAd], rIAd←rIAd+1,	c1; {Store in memory}
	uData ← MDR ← rTmp, rTmp ← IOPIData, BRANCH [$, WPgCross, 1],	c2; {Read next low byte}
WLo3:	rTmp ← rTmp LRot8, GOTO [WHiByte],	c3; {Left justify: Low, xxx} 

{This byte is the last byte.  Since we have to do one more click, set AlwaysWakeMode.  Have to put in an extra click to get everything done.  **Try to fix**.  There will be no page crossing, so do the memory reference and then go back to top of the loop.}

WLo3Last:	IOPCtl ← IOPAWMode,	c3; {xx}

	rTmp ← rTmp LRot8,	c1; {Flip bytes: High, Low}
	Noop,	c2;
	Noop,	c3;

WLoLast:	MAR← [RHrIAd, rIAd+0],	c1; {Store in memory}
	uData ← MDR ← rTmp,	c2; {Store data}
IOPDone:	IOPCtl ← IOPInMode, GOTO [IOPIdle],	c3; {Back to top}

{The previous MAR← instruction generated a page cross branch.  The data was not written.  The Data to be written is saved in uData, 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.  rIAd is now pointing to the beginning of this page, instead at the beginning of the next page.  We have to still write the last word of the previous page.  We will also put the remap code here later.  rTmp has the next low byte.}

WPgCross:	rIAd ← rIAd + 0FF,	c3; {Point to last address in page}

{No page cross possible on next instruction.}
	MAR← [RHrIAd, rIAd+0],	c1; {Store saved byte}
	MDR ← uData,	c2; {in last location on page}
	rIAd←rIAd+1,	c3; {Incr. address to new page} 

{Remap code here later.}
	Noop,	c1;
	GOTO [WLo3],	c2;


{**************************************************************************}

{COMMAND 1:  Read memory block.
     Format of transfer (bytes):
	command (4 bits), low addr (10:17), mid. addr (2:9), high addr (0:1),
	 low count-1 (8:15), high count-1 (0:7)
	 => returns:   low data, high data, .....

    IOP Control is in InMode}

{Read the low address (10:17)}
CReadMem:	rIAd ← IOPIData, CALL[IOPSubc2],	c1, at [1,4,IOPCmd];

	rTmp ← rTmp + rIAd + 1, IOPCtl ← IOPOutMode,	c3, at[1,10,IOPRet];

{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.  Data is sent to the port high byte first, then low byte.  At this point RHrIAd is set up with addr[0:1], rIAd with addr[2:17], rTmp has address of last word in buffer + 1.}

{Fetch the first word from the buffer.}
RFirst:	MAR ← [RHrIAd, rIAd], rIAd ← rIAd + 1,	c1; {Read word}
	uBufMax ← rTmp, BRANCH [$, RPgCrFirst, 1],	c2; {Store in uBufMax}
	rTmp ← MD,	c3;

{Read Memory Block Inner Loop}

{Low byte.  Output Low byte of word.}
RLoByte:	IOPOData ← rTmp LRot0,	c1; 
	Noop,	c2;
	[] ← rIAd xor uBufMax, ZeroBr,	c3; {Check for max. address}

{Process the high byte.  Check for page crossing as well.}
RHi:	MAR ← [RHrIAd, rIAd], rIAd←rIAd+1, BRANCH[$,RHi2Last],	c1; {Read next word}
	IOPOData ← rTmp LRot8, BRANCH [$, RPgCross, 1],	c2; {Output low byte}
	rTmp ← MD, GOTO [RLoByte],	c3; {Read next word}

{This byte is 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.}

RHi2Last:	IOPOData ← rTmp LRot8, CANCELBR [IOPDone, 2],	c2; {Output low byte}

{The first MAR← instruction generated a page cross branch.  rIAd is now pointing to the begining of this page, instead at the beginning of the next page.  Catch the first memory data, fix up rIAd to the correct address and continue.  We will also put the remap code here later.}

RPgCrFirst:	rTmp ← MD, GOTO [RPgCr1],	c3; {Save memory data}

{The previous MAR← instruction generated a page cross branch.  rIAd is now pointing to the begining of this page, instead at the beginning of the next page.  Catch the last memory data, fix up rIAd to the correct address and continue.  We will also put the remap code here later.}

RPgCross:	rTmp ← MD,	c3; {Save memory data}

RPgCr1:	rIAd ← rIAd + 0FF + 1,	c1; {Increment page number}
	Noop,	c2; {Saved byte}
	GOTO [RLoByte],	c3; {Increment pointer}


{**************************************************************************}

{COMMAND 2:  Do Naked Notify.
     Format of command:
	command (2 bits), low interrupt mask, high interrupt mask.

    IOP Control is in InMode}

{return from IOPSub with mask in rTmp; need to do another click}
	IOPCtl ← IOPAWMode,	c3, at[2,10,IOPRet];

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


{**************************************************************************}

{COMMAND 3:  (unimplemented)}

Command3:	Noop,		c1, at[3,10,IOPCmd];
IOPNoCmd:	GOTO[IOPDone],			c2;



{**************************************************************************}

{COMMAND 4:  (unimplemented)}

Command4:	GOTO[IOPNoCmd],		c1, at[4,10,IOPCmd];



{**************************************************************************}

{COMMAND 5:  Write Swapped Block}

WriteSwapped:	rIAd ← IOPIData, CALL[IOPSubc2],		c1, at[5,10,IOPCmd];

	rTmp ← rTmp + rIAd,	c3, at[5,10,IOPRet];

	uBufMax ← rTmp, GOTO[WSHighx],	c1;

{Write Swapped Block Inner Loop}

{Read and left justify high byte.  Test for end of buffer}
WSHigh:	[] ← rIAd xor uBufMax, ZeroBr,	c1;
WSHighx:	rTmp ← IOPIData, BRANCH[$,WSLast],	c2;
	rTmp ← rTmp LRot8,	c3; 

{Process low byte.  Check for page crossing as well.}
WSLo:	MAR← [RHrIAd, rIAd], rIAd←rIAd+1,	c1;
	rTmp ← MDR ← rTmp or IOPIData, BRANCH[$, WSCross, 1],	c2;
	uData ← rTmp, GOTO [WSHigh],	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.}

WSLast:	rTmp ← rTmp LRot8,	c3; {xx}

	MAR← [RHrIAd, rIAd+0],	c1;
	MDR ← rTmp or IOPIData, GOTO[IOPDone],	c2;

{The previous MAR← instruction generated a page cross branch.  The data was not written.  The Data to be written is saved in uData, 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.  rIAd is now pointing to the beginning of this page, instead at the beginning of the next page.  We have to still write the last word of the previous page.  We will also put the remap code here later.  rTmp has the next low byte.}

WSCross:	rIAd ← rIAd + 0FF,	c3; {Point to last address in page}

{No page cross possible on next instruction.}
	MAR← [RHrIAd, rIAd+0],	c1; 
	MDR ← rTmp,	c2;
	rIAd←rIAd+1, GOTO[WSHigh],	c3; 

{**************************************************************************}

{COMMAND 6:  Read Swapped Block}

ReadSwapped:	rIAd ← IOPIData, CALL[IOPSubc2],		c1, at[6,10,IOPCmd];

	rTmp ← rTmp + rIAd + 1, IOPCtl ← IOPOutMode,	c3, at[6,10,IOPRet];

{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.  Data is sent to the port high byte first, then low byte.  At this point RHrIAd is set up with addr[0:1], rIAd with addr[2:17], rTmp has address of last word in buffer + 1.}

{Fetch the first word from the buffer.}
RSFirst:	MAR ← [RHrIAd, rIAd], rIAd ← rIAd + 1,	c1; {Read word}
	uBufMax ← rTmp, BRANCH[$, RSCrFirst, 1],	c2; {Store in uBufMax}
	rTmp ← MD,	c3;

{Read Memory Block Inner Loop}

{Low byte.  Output Low byte of word.}
RSHigh:	IOPOData ← rTmp LRot8,	c1; 
	uData ← rTmp,	c2;
	[] ← rIAd xor uBufMax, ZeroBr,	c3; {Check for max. address}

{Process the high byte.  Check for page crossing as well.}
RSLo:	MAR ← [RHrIAd, rIAd], rIAd←rIAd+1, BRANCH[$,RSHi2Last],	c1; {Read next word}
	IOPOData ← uData, BRANCH[$, RSCross, 1],	c2;
	rTmp ← MD, GOTO[RSHigh],	c3;

{This byte is 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.}

RSHi2Last:	IOPOData ← uData, CANCELBR [IOPDone, 2],	c2; {Output low byte}

{The first MAR← instruction generated a page cross branch.  rIAd is now pointing to the begining of this page, instead at the beginning of the next page.  Catch the first memory data, fix up rIAd to the correct address and continue.  We will also put the remap code here later.}

RSCrFirst:	rTmp ← MD, GOTO [RSCr1],	c3; {Save memory data}

{The previous MAR← instruction generated a page cross branch.  rIAd is now pointing to the begining of this page, instead at the beginning of the next page.  Catch the last memory data, fix up rIAd to the correct address and continue.  We will also put the remap code here later.}

RSCross:	rTmp ← MD,	c3; {Save memory data}

RSCr1:	rIAd ← rIAd + 0FF + 1,	c1; {Increment page number}
	Noop,	c2; {Saved byte}
	GOTO [RSHigh],	c3; {Increment pointer}




{**************************************************************************
Address and count fetching subroutine
**************************************************************************}

{Read the low address (10:17)}
IOPCmd:
IOPSubc1:	rIAd ← IOPIData,	c1, at [0,10,IOPCmd];
IOPSubc2:	rIAd ← rIAd LRot8,	c2; {low, xxx}
	Noop,			c3;

{Read the middle address (2:9)}
	rIAd ← rIAd or IOPIData,	c1; {Low, High}
	rIAd ← rIAd LRot8,	c2; {High, Low}
	Noop,			c3;

{Read the high address (0:1) into RHrIAd}
	RHrIAd ← IOPIData,	c1;
	Noop,			c2;
	Noop,			c3;

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

{Read the low}
IOPSub2:	rTmp ← IOPIData,	c1, at[2,10,IOPCmd];
	rTmp ← rTmp LRot8,	c2; {Left justify}
	Noop,			c3;

{OR in the high}
	rTmp ← rTmp or IOPIData, L7Disp,	c1;
	rTmp ← rTmp LRot8, RET[IOPRet],	c2; {Flip bytes}


{****** Still to be added:
  -  page mapping}