{  IOCopy.mc, HGM,  4-Nov-84  2:58:57

This module implements a clump of opcodes used to copy data from/to main memory to/from Multibus IO addresses. The extra set of opcodes is because some devices (DES chip, SUN 3mb board) stuff all the data through 1 address, and others (3Com 10mb board) use a range of addresses.

"Read" means Read from IO device and write into memory.
"Write" means Write to IO device and read from memory.

IO Mapping can't cross 64K word boundries.  (Something is probably wrong if they cross 64K byte boundries.)

Main memory address must be valid if word count is 0.

Register usage:

rhRio, Rio	Used to address IO device
T		Holds data word
rhTT, TT/Q	Hold Virtual address
rhRx, Rx	Hold Real address
TOS		Has count of words left to transfer

All 8 operations are minimal stack.  Thus:

TOS		Count of words to transfer
uStack5	STK	High word of IO address
uStack4	STK-1	Low word of IO address
uStack3	STK-2	High word of main memory address
uStack2	STK-3	Low word of main memory address
Except for uStack2, the info on the stack is kept up to date.

L0, L1, and L3 used for Map Fixup
L2 used for mode dispatching

Beware: IO Addresses are WORD addresses rather than byte addresses.  Most boards will ignore the high bit in Rio and all of rhRio.  rhRio is there because the 3Com Ethernet boards eat up a lot of address space.  Fortunately, they respond to 24 bit addresses.  IO addresses can't cross 64K boundries.

Note that main memory address is on the bottom of the stack for both Reading and Writing.  This makes the microcode simpler, but adds an opportunity to get things mixed up if you expect the dest to be on the bottom.
}


Set[L2.ReadHole, 0];
Set[L2.ReadBlock, 1];
Set[L2.ReadHyper, 2];
Set[L2.WriteHole, 3];
Set[L2.WriteBlock, 4];
Set[L2.WriteHyper, 5];
Set[L2.ReadHoleSlow, 6];
Set[L2.WriteHoleSlow, 7];

ESC9n: {L2 loaded on the DISP4 to get here}
ReadHole:	TT ← uStack2{MemLo}, GOTO[IOCopyRestart],	c1, at[L2.ReadHole,10,ESC9n];
ReadBlock:	TT ← uStack2{MemLo}, GOTO[IOCopyRestart],	c1, at[L2.ReadBlock,10,ESC9n];
ReadHyper:	TT ← uStack2{MemLo}, GOTO[IOCopyRestart],	c1, at[L2.ReadHyper,10,ESC9n];
WriteHole:	TT ← uStack2{MemLo}, GOTO[IOCopyRestart],	c1, at[L2.WriteHole,10,ESC9n];
WriteBlock:	TT ← uStack2{MemLo}, GOTO[IOCopyRestart],	c1, at[L2.WriteBlock,10,ESC9n];
WriteHyper:	TT ← uStack2{MemLo}, GOTO[IOCopyRestart],	c1, at[L2.WriteHyper,10,ESC9n];
ReadHoleSlow:	TT ← uStack2{MemLo}, GOTO[IOCopyRestart],	c1, at[L2.ReadHoleSlow,10,ESC9n];
WriteHoleSlow:	TT ← uStack2{MemLo}, GOTO[IOCopyRestart],	c1, at[L2.WriteHoleSlow,10,ESC9n];
	
	PC ← PC - 1, GOTO[ESCb],			c1, at[08,10,ESC9n];
	PC ← PC - 1, GOTO[ESCb],			c1, at[09,10,ESC9n];
	PC ← PC - 1, GOTO[ESCb],			c1, at[0A,10,ESC9n];
	PC ← PC - 1, GOTO[ESCb],			c1, at[0B,10,ESC9n];
	PC ← PC - 1, GOTO[ESCb],			c1, at[0C,10,ESC9n];
	PC ← PC - 1, GOTO[ESCb],			c1, at[0D,10,ESC9n];
	PC ← PC - 1, GOTO[ESCb],			c1, at[0E,10,ESC9n];
	PC ← PC - 1, GOTO[ESCb],			c1, at[0F,10,ESC9n];
	
	
IOCopyRestart:
	rhTT ← uStack3{MemHi},						c2;
	L0 ← L0.IOCopy, GOTO[IOMap],					c3;
	
IONewPage:
	[] ← TT, ZeroBr, CANCELBR[$, 1],				c2;
	uStack2 ← TT, BRANCH[IOMap, IONewBank],				c3;
	
IONewBank:
	Q ← rhTT + 1, LOOPHOLE[byteTiming],				c1;
	rhTT ← Q LRot0,							c2;
	uStack3 ← Q,							c3;

IOMap:
	Map ← Q ← [rhTT, TT], L1 ← L1.None,				c1;
	rhRio ← uStack5{IOHi},						c2;
	Rx ← rhRx ← MD, XDirtyDisp,					c3;
	
	BRANCH[IOMud, $, 1]						c1, WLMFRet[L0.IOCopy];
	[] ← TOS, ZeroBr,						c2;
	BRANCH[IOCopy, IOCopyDone],					c3;


IOCopy:
	Rio ← uStack4, MesaIntBr, BRANCH[$, IONewPage],			c1;
	Q ← Rio + 1, L2Disp, BRANCH[$, IOCopyInt],			c2;
	DISP3[IOLoad],							c3;

IOLoadWord:
	IO ← [rhRio, Rio + 0], GOTO[IORdHole],				c1, at[L2.ReadHole, 8, IOLoad];
	IO ← [rhRio, Rio + 0], GOTO[IORd],				c1, at[L2.ReadBlock, 8, IOLoad];
	MAR ← [rhRio, Rio + 0], RawRef, GOTO[IORd],			c1, at[L2.ReadHyper, 8, IOLoad];
	MAR ← [rhRx, TT + 0], GOTO[IORdHole],				c1, at[L2.WriteHole, 8, IOLoad];
	MAR ← [rhRx, TT + 0], GOTO[IORd],				c1, at[L2.WriteBlock, 8, IOLoad];
	MAR ← [rhRx, TT + 0], GOTO[IORd],				c1, at[L2.WriteHyper, 8, IOLoad];
	IO ← [rhRio, Rio + 0], GOTO[IORdHole],				c1, at[L2.ReadHoleSlow, 8, IOLoad];
	MAR ← [rhRx, TT + 0], GOTO[IORdHole],				c1, at[L2.WriteHoleSlow, 8, IOLoad];


IORdHole:
	L2Disp, GOTO[IORda],						c2;
IORd:	uStack4 ← Q, L2Disp,						c2;
IORda:	T ← MD, DISP3[IOStore],						c3;
	
IOStoreWord:
	MAR ← [rhRx, TT + 0], GOTO[IOWr],				c1, at[L2.ReadHole, 8, IOStore];
	MAR ← [rhRx, TT + 0], GOTO[IOWr],				c1, at[L2.ReadBlock, 8, IOStore];
	MAR ← [rhRx, TT + 0], GOTO[IOWr],				c1, at[L2.ReadHyper, 8, IOStore];
	IO ← [rhRio, Rio + 0], GOTO[IOWr],				c1, at[L2.WriteHole, 8, IOStore];
	IO ← [rhRio, Rio + 0], GOTO[IOWr],				c1, at[L2.WriteBlock, 8, IOStore];
	MAR ← [rhRio, Rio + 0], RawRef, GOTO[IOWr],			c1, at[L2.WriteHyper, 8, IOStore];
	MAR ← [rhRx, TT + 0], GOTO[IOWr],				c1, at[L2.ReadHoleSlow, 8, IOStore];
	IO ← [rhRio, Rio + 0], GOTO[IOWr],				c1, at[L2.WriteHoleSlow, 8, IOStore];
	
IOWr:	MDR ← T, TOS ← TOS - 1, ZeroBr,					c2;
	TT ← TT + 1, PgCarryBr, BRANCH[IOCopy, IOCopyDone],		c3;
	
{-------------------------------------------------------------------------}

{Don't bother to remember if we are reading or writing}
{Map Update smashes L3.}
IOMud:	CALL[WLMapFix],							c2;

{-------------------------------------------------------------------------}

IOCopyInt:
	[] ← uWDC, NZeroBr, CANCELBR[$, 0F],				c3;
	
	uStack2 ← TT, BRANCH[$, IOCopyIgnoreInt],			c1;
	PC ← PC - 1, GOTO[IOCopyDoInt],					c2;
	
IOCopyFalseInt:
{Lots of registers have been smashed, but not L2}
	TT ← uStack2{MemLo}, GOTO[IOCopyRestart],			c1;
	
IOCopyIgnoreInt:
	Noop,								c2;
	ClrIntErr, GOTO[IOCopy],					c3;
	
{-------------------------------------------------------------------------}

IOCopyDone:
	stackP ← 0, CANCELBR[$, 1],					c1;
	Noop,								c2;
	Xbus ← uPCCross, XRefBr,					c3;

	PC ← PC - PC16, BRANCH[blNoInt, blSetInt {Block.mc}],		c1;

{-------------------------------------------------------------------------}