% File: MIOCAsync.mc

Edit by Danielson on March 19, 1981 1:25 PM:
Fix base reg fixup code.
Edit by Danielson on August 1, 1980 11:31 AM:
Leave external interrupts enabled.
Edit by Danielson on July 24, 1980 1:36 PM:
New enabling/disabling code for transmitter.
Edit by Danielson on June 5, 1980 5:19 PM:
Fix data lost code.
Edit by Danielson on April 22, 1980 1:40 PM:
Modified events to match new event definitions.
Edit by Danielson on February 14, 1980 5:29 PM

Contains Async RS232C code for MIOC board
1. IOCB type chaining
2. State table for Xerox 800 type frames
3. Single buffer/IOCB, single buffer/frame
%

insert[MIOCDefs];

Title [MIOCAsync];


SET TASK [0];

ONPAGE [IOPage];

IMRESERVE[IOPage,260,120];

* Registers

RV[IOOldBCC,1]; RV[IODataTemp,1]; RV[IOStatus,1];
RV[IONewBCC,2];

RV[IOBufBase,4];
RV[IOBufBaselo,5];
RV[IOMaxCount,6]; RV[IORegs,6];
RV[IOOffset,7]; RV[IONext,7];

RV[IOState,10];
RV[IOEvents,11];
RV[IODataByte,12];

MACRO[IOActionDisp,DISPATCH[#1,13,4]];
* Dispatch on event
MACRO[IOSizeDisp,DISPATCH[#1,10,2]];
* Character size dispatch
SET[IOActionLoc,ADD[LSHIFT[IOPage,10],220]];
* Event action type
SET [IOSizeLoc,ADD[LSHIFT[IOPage,10],200]];
* Char size

* Channel B nop code
* Satisfies interrupt on channel B if they occur
* Interrupt vectors = 0 - 3

CALLEXTERNAL [IOWrRegBLoc], T ← (IOResetXmtr), AT [IOIntrLoc,0];
GOTOEXTERNAL [IOIntrDoneLoc];
CALLEXTERNAL [IOWrRegBLoc], T ← (IOResetExt), AT [IOIntrLoc,2];
GOTOEXTERNAL [IOIntrDoneLoc];
NOP, AT [IOIntrLoc,6];
CALLEXTERNAL [IORdDataBLoc], AT [IOIntrLoc,4];
GOTOEXTERNAL [IOIntrDoneLoc];

* Initialize overlay: Zero IOMisc.

GOTOEXTERNAL [IOIntrDoneLoc], IOMisc ← 0C, AT [IOInitOverlayLoc];

* Start transmitter: Treat like transmitter interrupt

GOTO [IOXmtrStart], AT [IOStartXmtrLoc];

* Timeout occurred: Not used.

GOTOEXTERNAL [IOIntrDoneLoc], AT [IOTimeoutLoc];

* Interrupt vector = 4 (Transmitter has become empty). Set up
* IOEvents to load next character from IOCB and get new state.
* (Event bits 2 and 3).

IOXmtrStart: IOMisc ← (IOMisc) AND NOT (IOMiscInput), AT [IOIntrLoc,10];
PFetch1[IOcsb, IOiocb, IOcsbOut!], CALL [IODoTask];
GOTO [IODoIt], IOEvents ← OR[IOEventBit[2],IOEventBit[3]]C;

* Interrupt vector = 5 (External status change). Flag status
* event in CSB and reset external interrupts .

CALL [IODoTask], PFetch1[IOcsb, IOStatus, IOcsbStat!], AT [IOIntrLoc,12];
IOStatus ← (IOStatus) OR (IOStatEvent);
PStore1[IOcsb,IOStatus, IOcsbStat!];
CALLEXTERNAL [IOWrRegALoc], T ← (IOResetExt); * Reset to check for more
IODoInt: CALLEXTERNAL [IODoNotifyLoc], PFetch1[IOcsb, IOInt1, IOcsbMask!];
GOTOEXTERNAL [IOIntrDoneLoc];

* Interrupt vector = 7. (Special received condition). Save status
* for the Mesa face and then treat as normal character.

CALLEXTERNAL [IOWrRegALoc], T ← 1C, AT [IOIntrLoc,16];
PFetch1[IOcsb, IOiocb, IOcsbIn!];
CALLEXTERNAL [IORdRegALoc], T ← 0C;
CALL[IOSaveIOCBStat], LU ← IOiocb;
CALLEXTERNAL [IOWrRegALoc], T ← (IOResetError);
NOP;

* Interrupt vector = 6 (Receiver FIFO not empty).
* Set up IOEvents to get new state. (Event bit 3).
* Also, due to a misfeature of the SIO chip, we have to turn off the unused bits of the character
* based on the character length.

IOInIntr: PFetch1[IOcsb, IOState, IOcsbRegs!], AT [IOIntrLoc,14];
CALLEXTERNAL [IORdDataALoc], IOMisc ← (IOMisc) OR (IOMiscInput);
IODataByte ← T, TASK;
PFetch1[IOcsb, IOiocb, IOcsbIn!];
IOSizeDisp[IOState];
DISP [.+1], IOEvents ← IOEventBit[3]C;
IOSize5: GOTO [IODoIt], IODataByte ← (IODataByte) AND (37C), AT [IOSizeLoc,0];
IOSize7: GOTO [IODoIt], IODataByte ← (IODataByte) AND (177C), AT [IOSizeLoc,1];
IOSize6: GOTO [IODoIt], IODataByte ← (IODataByte) AND (77C), AT [IOSizeLoc,2];
IOSize8: GOTO [IODoIt], IODataByte ← (IODataByte) AND (377C), AT [IOSizeLoc,3];

* This is the main loop for the microcode.
* Upon entry, IOEvents tells the microcode the actions to be done.
* To simplify the microcode, one of the actions recalculates the
* next state and actions based on the current values in IODataByte
* and IOState and then branches to IODoIt to restart.

IODoIt: LU ← IOiocb;
GOTO [IONoIOCB,ALU=0], IOMisc ← (IOMisc) AND NOT (IOMiscCount);
PFetch4[IOiocb, IOBufBase, IOIOCBBuffer!], CALL[IODoTask];
T ← RHMASK[IOBufBaseLo];
IOBufBaseLo ← (LSH[IOBufBaseLo,10]) + T + 1;
IOCheckEvent: CALL [IODoTask];
DBLGOTO [IODoNext,IODoThis,R>=0], IOEvents ← LSH[IOEvents,1];
IODoNext: GOTO [IOCheckEvent], IOMisc ← (IOMisc) + (2C);
IODoThis: IOActionDisp[IOMisc];
DISP[.+1];

* Event bit 0
* Ok to send fill sequence BEFORE this character
* Not implemented in asynchronous case

GOTO [IODoNext], AT [IOActionLoc,0];

* Event bit 1
* Back up a character in IOCB by decrementing IOOffset.
* This state has two uses.
* 1. On input, if a character is stored in the IOCB buffer and it
* is later determined that the character should not be in the
* buffer, the offset is decremented to "remove" the character
* from the buffer. An example of this is DLE SYN. When the DLE
* is received it is placed in the output buffer. If the next
* character is a SYN, then this state is used to decrement
* IOOffset, "removing" the DLE from the buffer.
* 2. On output, to simplify microcode, a character is always
* fetched from the buffer. If we decide not to send this
* character right away, but instead send a special character
* such as the CRC or a DLE, the offset into the buffer is
* decremented such that next time through the loop, the
* microcode will fetch the character again.

GOTO [IOSaveOffset], IOOffset ← (IOOffset) - 1, AT [IOActionLoc,1];

* Event bit 2
* Load/Store next character in IOCB.
* Checks if there is any buffer space left and if so, branches to
* input/output routine that retrieves/stores next byte to the IOCB
* buffer. If no space available, branch to no buffer space code.

T ← IOMaxCount, AT [IOActionLoc,2];
LU ← (IOOffset) - T;
DBLGOTO [IONoSpace, IOSpace, ALU=0], T ← RSH[IOOffSet, 1]; * Handle no space
IOSpace: PFetch1[IOBufBase, IODataTemp], CALL [IODoTask];
DBLGOTO [IOISpace, IOOSpace, R ODD], LU ← IOMisc;

* No IOCB condition:
* On input, set data lost in CSB and notify head via naked notify.
* On output, satisfy transmitter interrupt and disable transmitter.

IONoIOCB: DBLGOTO[IOINoIOCB, IOONoIOCB, R ODD], LU ← IOMisc;
IOINoIOCB: PFetch1[IOcsb, IOStatus, IOcsbStat!];
LU ← (IOStatus) AND (IOStatDataLost);
GOTO [IOIDataLost, ALU#0], IOStatus ← (IOStatus) OR (IOStatDataLost);
NOP;
PStore1[IOcsb, IOStatus, IOcsbStat!];
IOStatus ← IOStatus;
GOTO [IODoInt];
IOIDataLost: GOTOEXTERNAL [IOIntrDoneLoc];
IOONoIOCB: IOMisc ← (IOMisc) AND NOT (IOMiscEnabled);
PFetch1[IOcsb, IORegs, IOcsbRegs!];
CALLEXTERNAL [IOWrRegALoc], T ← OR[IOResetXmtr!,5]C;
CALLEXTERNAL [IOWrRegALoc], T ← RSH[IORegs,10];
GOTOEXTERNAL [IOIntrDoneLoc];

* No space in IOCB condition:
* On input, set data lost in IOCB by faking receiver overrun.
* On output, force end of IOCB

IONoSpace: DBLGOTO[IOINoSpace, IOONoSpace, R ODD], LU ← IOMisc;
IOINoSpace: CALL [IOSaveIOCBStat], T ← 40C;
* Rcvr overrun bit
GOTO [IODoNext];
IOONoSpace: GOTO [IODoneIOCB];

* Room in buffer (input case).
* Stores a single byte in buffer.
* IOOffset contains offset into buffer in bytes.

IOISpace: GOTO [IOIEven, R EVEN], LU ← IOOffset;
IOIOdd: T ← RHMASK[IODataByte];
GOTO [IOIBoth], IODataTemp ← (LHMASK[IODataTemp]) OR T;
IOIEven: T ← LSH[IODataByte,10];
GOTO [IOIBoth], IODataTemp ← (RHMASK[IODataTemp]) OR T;
IOIBoth: T ← RSH[IOOffset,1];
GOTO [IONewOffset], PStore1[IOBufBase, IODataTemp];

* Room in buffer (output case).
* Gets next byte from buffer.
* IOOffset contains offset into buffer in bytes.

IOOSpace: GOTO [IOOEven, R EVEN], LU ← IOOffset;
IOOOdd: GOTO [IOOBoth], T ← RHMASK[IODataTemp];
IOOEven: GOTO [IOOBoth], T ← RSH[IODataTemp,10];
IOOBoth: IODataByte ← T;
IONewOffset: IOOffset ← (IOOffset) + 1;
IOSaveOffset: GOTO [IODoNext], PStore1[IOiocb, IOOffset, IOIOCBOffset!];

* Event bit 3
* Get new state and restart. The character value is converted to a
* character type by looking it up in a 256 word table located at
* offset 400B-777B in the tables. This character type is combined
* with the old state to give the offset to the next state entry.
* This entry contains the next state, special character info, and
* the events to preform.

IONextState: T ← (IODataByte) + (400C), AT [IOActionLoc,3]; * Char type entry offset
CALL [IODoTask], PFetch2[IOcsb, IOBufBase, IOcsbTables!];
CALL [IODoTask], PFetch1[IOiocb, IOState, IOIOCBState!];
CALL [IODoTask], IOState ← RSH[IOState,3];
CALL [IODoTask], PFetch1 [IOBufBase,IOTemp0];
CALL [IODoTask], T ← (IOState) AND NOT (37C);
T ← (IOTemp0) + (T);
CALL [IODoTask], PFetch2[IOBufBase, IOState];
GOTO [IODoIt];

* Event bit 4
* Update BCC for this character.
* Formula to calculate the BCC is:
* NewBCC ← BCCTable[(Data XOR OldBCC) AND 377B] XOR (RSH[OldBCC,10B]);
* The BCCTable is located at offsets 0-377B in the tables.

IODoBCC: T ← RHMASK[IODataByte], AT [IOActionLoc,4];
CALL [IODoTask], PFetch2[IOcsb, IOBufBase, IOcsbTables!];
CALL [IODoTask], PFetch1[IOiocb, IOOldBCC, IOIOCBBCC!];
CALL [IODoTask], T ← (RHMASK[IOOldBCC]) XOR (T);
CALL [IODoTask], PFetch1[IOBufBase,IONewBCC];
CALL [IODoTask], T ← (IONewBCC);
IOOldBCC ← (RSH[IOOldBCC,10]) XOR T;
IOSaveCRC: GOTO [IODoNext], PStore1[IOiocb, IOOldBCC, IOIOCBBCC!]; * Save new checksum

* Event bit 5
* Zero the BCC

IOOldBCC ← 0C, GOTO [IOSaveCRC], AT [IOActionLoc,5];

* Event bit 6
* Enable transmitter.
* Output the character just loaded from the IOCB buffer to SIO chip.

IOSendData: PFetch1[IOcsb, IORegs, IOcsbRegs!], AT [IOActionLoc,6];
CALLEXTERNAL [IOWrRegALoc], T ← 5C;
T ← 10C;* Xmtr enable bit
CALLEXTERNAL [IOWrRegALoc], T ← (RSH[IORegs,10]) OR (T);
CALLEXTERNAL [IOWrDataALoc], T ← IODataByte;
GOTO [IODoNext], IOMisc ← (IOMisc) OR (IOMiscEnabled);

* Event bit 7
* Send special character. The special character is located
* in the right half of the second word fetched from the state
* table.

T ← RHMASK[IOState], AT [IOActionLoc,7];
GOTO [IOSendData], IODataByte ← T;

* Event bit 10
* Send BCC character and shift BCC right by 8 to align next byte
* of BCC to be sent.

PFetch1[IOiocb, IOOldBCC, IOIOCBBCC!], AT [IOActionLoc,10];
CALLEXTERNAL [IOWrDataALoc], T ← RHMASK[IOOldBCC];
GOTO [IOSaveCRC], IOOldBCC ← RSH[IOOldBCC,10];

* Event bit 11
* Set SYN seen.
* This event is not implemented in asynchronous microcode.

GOTO [IODoNext], AT [IOActionLoc,11];

* Event bit 12
* End frame. Remove IOCB from chain and issue naked notify.

IODoneIOCB: IOState ← (ZERO) - 1, AT [IOActionLoc,12];
* Set processed
CALL [IODoTask], PFetch1[IOiocb, IONext, IOIOCBNext!];
CALL [IODoTask], PFetch1[IOcsb, IOInt1, IOcsbMask!];
CALL [IODoTask], LU ← IONext;* Get ptr to next IOCB
DBLGOTO[.+1,.+2, R ODD], LU ← IOMisc;* Update CSB
GOTO [.+2], Pstore1[IOcsb, IONext, IOcsbIn!];
GOTO [.+1], Pstore1[IOcsb, IONext, IOcsbOut!];
CALLEXTERNAL [IODoNotifyLoc]; * Do naked notify
NOP;

* Event bit 13
* Save new state in IOCB and return from interrupt

GOTOEXTERNAL [IOIntrDoneLoc], PStore1[IOiocb, IOState, IOIOCBState!], AT [IOActionLoc,13];

IOOutTask: NOP;
IODoTask: RETURN;

IOSaveIOCBStat: GOTO [IODoTask, ALU=0];
PFetch1[IOiocb, IOTemp0, IOIOCBStat!];
IOTemp0 ← (IOTemp0) OR (T);
RETURN, PStore1[IOiocb, IOTemp0, IOIOCBStat!];

:END[MIOCAsync];