XBus:
CEDAR DEFINITIONS =
BEGIN
Note: For Multibus IORead and IOWrite operations the Busmaster card consumes the low 256 bytes of the IO address space. For PC operations the entire IO address space is free for use. Use BusmasterRead and BusmasterWrite to access the on board devices.
The Multibus1/PCBus block-moving opcodes cause an opcode trap when an MBus timeout occurs due to the absence of an acknowledgment during a 10 microsecond wait; this is considered a fatal hardware malfunction. The opcode trap procedure executed when this occurs raises this signal.
MBusTimeout: SIGNAL;
Note: For Multibus IORead and IOWrite operations the Busmaster card consumes the low 256 bytes of the IO address space. For PC operations the entire IO address space is free for use. Use BusmasterRead and BusmasterWrite to access the on board devices.
IORead:
PROC [mbFrom:
LONG
POINTER]
RETURNS [data:
WORD] =
TRUSTED INLINE
{
ReadMBus[8604H, @data, mbFrom, 2, 1];
};
IOWrite:
PROC [mbTo:
LONG
POINTER, data:
WORD] =
TRUSTED INLINE
{
WriteMBus[8508H, @data, mbTo, 2, 1];
};
MemRead:
PROC [mbFrom:
LONG
POINTER]
RETURNS [data:
WORD] =
TRUSTED
INLINE {
ReadMBus[8A01H, @data, mbFrom, 2, 1];
};
MemWrite:
PROC [mbTo:
LONG POINTER, data:
WORD] =
TRUSTED INLINE {
WriteMBus[8902H, @data, mbTo, 2, 1];
};
MoveToMB:
PROC [from, to:
LONG
POINTER, wordCount:
CARDINAL] =
TRUSTED
INLINE {
WriteMBus[8902H, from, to, 2, wordCount];
};
MoveFromMB:
PROC [from, to:
LONG
POINTER, wordCount:
CARDINAL] =
TRUSTED
INLINE {
ReadMBus[8A01H, to, from, 2, wordCount];
};
BusMasterRead:
PROC [mbFrom:
LONG
POINTER]
RETURNS [data:
WORD] =
TRUSTED INLINE
{
ReadMBus[404H, @data, mbFrom, 2, 1];
};
BusMasterWrite:
PROC [mbTo:
LONG
POINTER, data:
WORD] =
TRUSTED INLINE
{
WriteMBus[408H, @data, mbTo, 2, 1];
};
These procedures read/write one byte from/to the PC bus using bits [8..15] in the VM word. Note that this is done using the Multibus1 opcodes.
PCIORead1B:
PROC [mbFrom:
LONG
POINTER]
RETURNS [data:
WORD] =
TRUSTED INLINE
{
ReadMBus[4H, @data, mbFrom, 2, 1];
};
PCIOWrite1B:
PROC [mbTo:
LONG
POINTER, data:
WORD] =
TRUSTED
INLINE {
WriteMBus[8H, @data, mbTo, 2, 1];
};
PCMemRead1B:
PROC [mbFrom:
LONG
POINTER]
RETURNS [data:
WORD] =
TRUSTED
INLINE {
ReadMBus[1H, @data, mbFrom, 2, 1];
};
PCMemWrite1B:
PROC [mbTo:
LONG
POINTER, data:
WORD] =
TRUSTED
INLINE {
WriteMBus[2H, @data, mbTo, 2, 1];
};
These procedures read/write one 16-bit word from/to the PC bus using the special block move opcodes for the PC bus.
PCIORead:
PROC [mbFrom:
LONG
POINTER]
RETURNS [data:
WORD] =
TRUSTED INLINE
{
ReadIBMBus[4H, @data, mbFrom, 1, 1];
};
PCIOWrite:
PROC [mbTo:
LONG
POINTER, data:
WORD] =
TRUSTED
INLINE {
WriteIBMBus[8H, @data, mbTo, 1, 1];
};
PCMemRead:
PROC [mbFrom:
LONG
POINTER]
RETURNS [data:
WORD] =
TRUSTED
INLINE {
ReadIBMBus[1H, @data, mbFrom, 1, 1];
};
PCMemWrite:
PROC [mbTo:
LONG POINTER, data:
WORD] =
TRUSTED INLINE {
WriteIBMBus[2H, @data, mbTo, 1, 1];
};
These procedures move blocks of 16-bit words.
PCMoveToMB:
PROC [from, to:
LONG
POINTER, wordCount:
CARDINAL] =
TRUSTED INLINE
{
WriteIBMBus[2H, from, to, 1, wordCount];
};
PCMoveFromMB:
PROC [from, to:
LONG
POINTER, wordCount:
CARDINAL] =
TRUSTED
INLINE {
ReadIBMBus[1H, to, from, 1, wordCount];
};
PCRefreshOn:
PROC [];
Initializes a DMA controller on the Busmaster card to perform continuous memory refresh on the IBM-PC bus.
These routines manipulate registers on the XBus interface card. You shouldn't need to use them.
ReadL: PROC [add: POINTER] RETURNS [WORD] = TRUSTED MACHINE CODE {PrincOps.zMISC, XOps.aReadL};
ReadM: PROC [add: POINTER] RETURNS [WORD] = TRUSTED MACHINE CODE {PrincOps.zMISC, XOps.aReadM};
WriteL: PROC [add: POINTER, data: WORD] = TRUSTED MACHINE CODE {PrincOps.zMISC, XOps.aWriteL};
WriteM: PROC [add: POINTER, data: WORD] = TRUSTED MACHINE CODE {PrincOps.zMISC, XOps.aWriteM};
The ReadMBus and WriteMBus opcodes move blocks of data between the MultiBus1 and Dandelion VM or a single byte between the PC bus and Dandelion VM. The ReadIBMBus and WriteIBMBus opcodes move blocks of words between the PCBus and Dandelion VM. Most microcode is common to all opcodes. Clients will ordinarily use the opcodes indirectly by calling the other INLINE procedures above in which the controlData parameter is specified. byteInterval is the separation in bytes between consecutive source words (MBus) or bytes (PCBus).
Timing ~= 10 clicks + 6 clicks/word + 3 clicks/page cross + 2 clicks/ackwait
~= 2.5 microseconds/word
WriteMBus: PROC [controlData: WORD, from, to: LONG POINTER, byteInterval, wordCnt: CARDINAL] = TRUSTED MACHINE CODE {PrincOps.zMISC, XOps.aWriteMBus};
Timing ~= 10 clicks + 8 clicks/word + 3 clicks/page cross + 2 clicks/ackwait
~= 3.3 microseconds/word
ReadMBus: PROC [controlData: WORD, to, from: LONG POINTER, byteInterval, wordCnt: CARDINAL] RETURNS [] = TRUSTED MACHINE CODE {PrincOps.zMISC, XOps.aReadMBus};
Timing ~= 10 clicks + 11 clicks/word + 3 clicks/page cross + 2 clicks/ackwait
~= 4.5 microseconds/word
WriteIBMBus: PROC [controlData: WORD, from, to: LONG POINTER, byteInterval, wordCnt: CARDINAL] = TRUSTED MACHINE CODE {PrincOps.zMISC, XOps.aWriteIBMBus};
Timing ~= 10 clicks + 15 clicks/word + 3 clicks/page cross + 2 clicks/ackwait
~= 6.2 microseconds/word
ReadIBMBus: PROC [controlData: WORD, to, from: LONG POINTER, byteInterval, wordCnt: CARDINAL] RETURNS [] = TRUSTED MACHINE CODE {PrincOps.zMISC, XOps.aReadIBMBus};
The implementation of the opcodes is shown by the following pseudo-program
Read/Write M/PC Bus: PROC [controlData: WORD, VMaddr, MBaddr: LONG PONTER, wordCount: CARDINAL] ~ {
IF wordCount = 0 THEN RETURN;
WriteM[mBusCtl {2}, controlData];
WriteM[mBusAddr {3}, HighHalf[MBaddr]];
GOTO[Enter];
LP:
IF (wordCount ← wordCount - 1) = 0 THEN RETURN;
IF (LowHalf[MBaddr] ← LowHalf[MBaddr] + byteInterval) carries THEN {
HighHalf[MBaddr] ← HighHalf[MBaddr] + 1;
WriteM[mBusAddr {3}, HighHalf[MBaddr]];
};
IF (VMaddr ← VMaddr + 1) crosses page boundary THEN {
Enter:
Remap[VMaddr]; --Different for reads and writes
};
IF interrupt pending and enabled THEN service it;
outData: WORD ← VMaddr^;
WriteL[1BusAddr {3}, LowHalf[MBaddr]];
WriteL[1BusOutData {2}, outData]; --or outData LCY 8 if PCBus
For 10 microcoseconds loop; after that return error. Normally ack is given in 0.5 to 1.0 microseconds.
FOR I: CARDINAL IN [0..12) DO
IF BITAND[ReadM[mBusCtl {2}], 4] # 0 THEN { --Ack received
XXX; --Code specific to the opcode
};
ENDLOOP;
--MISC opcode trap to a procedure which raises the MBusTimeout signal
Trap[MBusTimeout];
};
For WriteMBus XXX =
GOTO[Lp];
For ReadMBus XXX =
VMaddr^ ← ReadL[1BusInData]; GOTO[Lp];
For WritePCBus XXX =
IF (LowHalf[MBaddr] ← LowHalf[MBaddr] + byteInterval) carries THEN {
HighHalf[MBaddr] ← HighHalf[MBaddr] + 1;
WriteM[mBusAddr {3}, HighHalf[MBaddr]];
};
WriteL[1BusAddr {3}, LowHalf[MBaddr]];
WriteL[1BusOutData {2}, outData]; --Write the odd byte
FOR I: CARDINAL IN [0..12) DO
IF BITAND[ReadM[mBusCtl {2}], 4] # 0 THEN GOTO[Lp]; --Ack received
ENDLOOP;
Trap[MBusTimeout];
For ReadPCBus XXX =
evenByte: WORD ← BITSHIFT[ReadL[1BusInData {2}], 8];
IF (LowHalf[MBaddr] ← LowHalf[MBaddr] + byteInterval) carries THEN {
HighHalf[MBaddr] ← HighHalf[MBaddr] + 1;
WriteM[mBusAddr {3}, HighHalf[MBaddr]];
};
WriteL[1BusAddr {3}, LowHalf[MBaddr]];
WriteL[1BusOutData {2}, garbage];
FOR I: CARDINAL IN [0..12) DO
IF BITAND[ReadM[mBusCtl {2}], 4] # 0 THEN { --Ack received
VMaddr^ ← BITOR[evenByte, BITAND[ReadL[1BusInData {2}], 377B]];
GOTO[Lp];
};
ENDLOOP;
Trap[MBusTimeout]; --Trap through the MISC opcode trap table
END.