% File: MIOCBit.mc

Edit by Danielson on March 19, 1981 1:25 PM:
Fix base reg fixup code.
Edit by Danielson on August 1, 1980 11:36 AM
Always leave external status interrupts enabled.
Edit by Danielson on July 25, 1980 1:38 PM
Half-duplex -- interframe fill with FF.
Edit by Danielson on June 5, 1980 3:29 PM
Fix data lost code
Edit by Danielson on April 18, 1980 3:40 PM

Contains Bit RS232C code for MIOC board
1. IOCB type chaining
2. Single buffer/IOCB, single buffer/frame
3. BCC calculation done by SIO chip
%

insert[MIOCDefs];

Title [MIOCBit];

SET TASK [0];

ONPAGE [IOPage];

IMRESERVE[IOPage,260,120];

* Registers


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

RV[IODataByte,10]; RV[IOStart,10];
RV[IORegs,11];
RV[IOStatus,12];

MACRO[IOSizeDisp,DISPATCH[#1,10,2]];
* Character size dispatch
MACRO[IOEOFDisp,DISPATCH[#1,5,3]];
* EOF dispatch
SET [IOSizeLoc,ADD[LSHIFT[IOPage,10],220]];
* Char size
SET [IOEOFLoc,ADD[LSHIFT[IOPage,10],200]];
* EOF

* 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: Reset IOMisc bits

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

* Start transmitter: Treat as normal transmitter empty interrupt.
* Also, reset Xmtr CRC generator.

NOP, AT [IOStartXmtrLoc];
CALLEXTERNAL[IOWrRegALoc], T ← (IOResetXmtrCRC);
GOTO [IONeedChar];

* Timeout occurred: Generate fake transmitter interrupt if needed.

NOP, AT [IOTimeoutLoc];* Bypass NOP
LU ← (IOMisc) AND (IOMiscEOFTimeout);* Check for doing EOF.
GOTO [IOFakeInterrupt, ALU#0];* If so, fake an interrupt
GOTOEXTERNAL [IOIntrDoneLoc];* Else ignore
IOFakeInterrupt: GOTO [IONeedChar];

* Interrupt vector = 4 (Transmitter has become empty).
* 1. If no IOCB, satisfy interrupt by ResetXmtr command.
* 2. If no more data to send in IOCB, satisfy interrupt by
* ResetXmtr, issue ResetUnderrun
* to force chip to send CRC, and set endOfFrame bit.
* 3. If end of frame bit interrupt, complete the IOCB, and check for any more frames.

IONeedChar: IOMisc ← (IOMisc) AND NOT (IOMiscInput), AT [IOIntrLoc,10];
GOTO [IOLoadIOCB], PFetch1[IOcsb, IOiocb, IOCSBOut!];

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

PFetch1[IOcsb, IOStatus, IOcsbStat!], AT [IOIntrLoc,12];
CALLEXTERNAL[IORdRegALoc];
LU ← (IOTemp0) AND (200C);* Check Break bit
GOTO [IONotBreak, ALU=0], IOStatus ← (IOStatus) OR (IOStatEvent);
PFetch1[IOcsb, IOiocb, IOCSBIn!], CALL [IODoTask];
LU ← IOiocb;
GOTO [IONotBreak1,ALU=0];
PFetch1[IOiocb, IOStart, IOIOCBStart!], CALL [IODoTask];
LU ← IOStart;
PStore1[IOiocb, IOStart, IOIOCBOffset!];
IONotBreak1: NOP;
IONotBreak: NOP;
CALL [IODoTask], PStore1[IOcsb, IOStatus, IOcsbStat!];
CALLEXTERNAL [IOWrRegALoc], T ← (IOResetExt); * Reset external interrupts
IODoInt: CALLEXTERNAL [IODoNotifyLoc], PFetch1[IOcsb, IOInt1, IOcsbMask!]; * Do naked notify
GOTOEXTERNAL [IOIntrDoneLoc];

* Interrupt vector = 7. (Special received condition).
* 1. Save status for the Mesa face.
* 2. Issue ResetError command to clear error.

CALLEXTERNAL [IOWrRegALoc], T ← 1C, AT [IOIntrLoc,16];
NOP;
CALLEXTERNAL [IORdRegALoc];
IOStatus ← T;
CALLEXTERNAL [IOWrRegALoc], T ← (IOResetError);

* Read in character

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

* Interrupt vector = 6 (Receiver FIFO not empty).

GOTO [IOInIntr], IOStatus ← 0C, AT [IOIntrLoc,14];

* 1. If no IOCB, then signal data lost
* 2. If IOCB buffer full, then signal data lost
* 3. Otherwise, store next character in buffer.

IOLoadIOCB: LU ← IOiocb;
GOTO [IONoIOCB,ALU=0];
PFetch4[IOiocb, IOBufBase, IOIOCBBuffer!], CALL[IODoTask];
T ← RHMASK[IOBufBaseLo];
IOBufBaseLo ← (LSH[IOBufBaseLo,10]) + T + 1;
T ← IOMaxCount;
LU ← (IOOffset) - T;
DBLGOTO [IONoSpace, IOSpace, ALU=0], T ← RSH[IOOffSet, 1];
IOSpace: CALL [IODoTask], PFetch1[IOBufBase, IODataTemp];
DBLGOTO [IOISpace, IOOSpace, R ODD], LU ← IOMisc;

* No IOCB condition:
* On input, set data lost in CSB.
* On output, issue ResetXmtr command to satisfy SIO transmitter interrupt.

IONoIOCB: DBLGOTO[IOINoIOCB, IOONoIOCB, R ODD], LU ← IOMisc;
IOINoIOCB: PFetch1[IOcsb, IORegs, IOcsbRegs!];
CALLEXTERNAL[IOWrRegALoc], T ← 3C;* Write register 3
CALLEXTERNAL[IOWrRegALoc], T ← RHMASK[IORegs];* Set chip to look for SYN
CALL [IODoTask], PFetch1[IOcsb, IOStatus, IOcsbStat!];
LU ← (IOStatus) AND (IOStatDataLost);
GOTO [IOIDataLost, ALU#0], IOStatus ← (IOStatus) OR (IOStatDataLost);
PStore1[IOcsb, IOStatus, IOcsbStat!];
GOTO [IODoInt];
IOIDataLost: GOTOEXTERNAL [IOIntrDoneLoc];
IOONoIOCB: PFetch1[IOcsb, IORegs, IOcsbRegs!];
CALLEXTERNAL [IOWrRegALoc], T ← OR[IOResetXmtr!,5]C;
CALLEXTERNAL [IOWrRegALoc], T ← RSH[IORegs,10];
GOTOEXTERNAL [IOIntrDoneLoc], IOMisc ← (IOMisc) AND NOT (IOMiscEnabled);

* No space in IOCB condition:
* On input, set data lost in IOCB by faking receiver overrun.
* On output, process EOF.

IONoSpace: DBLGOTO[IOINoSpace, IOONoSpace, R ODD], IOEOFDisp[IOMisc];
IOINoSpace: CALL [IOSaveIOCBStat], T ← 40C;
* Rcvr overrun bit
GOTO [IOCheckEnd];
IOONoSpace: DISP[IOEOFOut];

* 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).
* Enables transmitter.
* Gets next byte from buffer.
* IOOffset contains offset into buffer in bytes.

IOOSpace: GOTO [IOOEven, R EVEN], LU ← IOOffset;
IOOOdd: GOTO [IOOBoth], IODataTemp ← RHMASK[IODataTemp];
IOOEven: GOTO [IOOBoth], IODataTemp ← RSH[IODataTemp,10];
IOOBoth: PFetch1[IOcsb, IORegs, IOcsbRegs!];
CALLEXTERNAL[IOWrRegALoc], T ← 5C;
T ← 10C;* Enable tranmitter
CALLEXTERNAL[IOWrRegALoc], T ← (RSH[IORegs,10]) OR (T);
IOMisc ← (IOMisc) OR (IOMiscEnabled);
CALLEXTERNAL [IOWrDataALoc], T ← IODataTemp;
IONewOffset: IOOffset ← (IOOffset) + 1;
IOSaveOffset: CALL [IODoTask], PStore1[IOiocb, IOOffset, IOIOCBOffset!];
DBLGOTO [IOCheckEnd, .+1, R ODD], LU ← IOMisc;
GOTOEXTERNAL [IOIntrDoneLoc];

* Check for end of input frame.
* The SIO chip tells us when EOF occurs by setting the 200 bit in the status word.

IOCheckEnd: LU ← (IOStatus) AND (200C);
* Input end of file?
GOTO [IOIDoneIOCB, ALU#0];
GOTOEXTERNAL [IOIntrDoneLoc];* No
IOIDoneIOCB: GOTO [IODoneIOCB], PStore1 [IOiocb, IOStatus, IOIOCBBCC!]; * Yes

* Handle EOF on output
* States are:
* 0 -> Starting EOF. Reset chip to send CRC and increment state.
* 2 -> Chip starting to send ending flag. If last IOCB in chain,
* disable transmitter. Increment state to enable timeouts.
* 4 -> First timeout, do nothing but increment state.
* 6 -> Second timeout. End the frame.

IOEOFOut: CALLEXTERNAL [IOWrRegALoc], T ← IOResetXmtr, AT [IOEOFLoc,0];
NOP;
CALLEXTERNAL [IOWrRegALoc], T ← IOResetUnderrun;
GOTOEXTERNAL [IOIntrDoneLoc], (IOMisc) ← (IOMisc) + (IOMiscEOFIncr);
CALL [IODoTask], PFetch1[IOiocb, IONext, IOIOCBNext!], AT [IOEOFLoc,2];
LU ← IONext;
GOTO [IOMoreToSend, ALU#0];
PFetch1[IOcsb, IORegs, IOcsbRegs!];
CALLEXTERNAL [IOWrRegALoc], T ← OR[IOResetXmtr!,5]C;
CALLEXTERNAL [IOWrRegALoc], T ← RSH[IORegs,10];
IOMisc ← (IOMisc) AND NOT (IOMiscEnabled);
IOMoreToSend: GOTOEXTERNAL [IOIntrDoneLoc], IOMisc ← (IOMisc) + (IOMiscEOFIncr);
GOTOEXTERNAL [IOIntrDoneLoc], IOMisc ← (IOMisc) + (IOMiscEOFIncr), AT [IOEOFLoc,4];
GOTO [IODoneIOCB], IOMisc ← (IOMisc) AND NOT (IOMiscEOFBits), AT [IOEOFLoc,6];

* End of frame.
* Chain to next IOCB and issue naked notify.

IODoneIOCB: IOStatus ← (ZERO) - 1;
CALL [IODoTask], PFetch1[IOiocb, IONext, IOIOCBNext!];
CALL [IODoTask], PFetch1[IOcsb, IOInt1, IOcsbMask!]; * Do naked notify
CALL [IODoTask], LU ← IONext;* Get ptr to next IOCB
DBLGOTO[IODoneIn,IODoneOut, R ODD], LU ← IOMisc;* Update CSB
IODoneIn: Pstore1[IOcsb, IONext, IOcsbIn!];
CALLEXTERNAL [IODoNotifyLoc];
GOTOEXTERNAL [IOIntrDoneLoc], PStore1[IOiocb, IOStatus, IOIOCBState!];
IODoneOut: GOTO [.+1], Pstore1[IOcsb, IONext, IOcsbOut!];
CALLEXTERNAL [IODoNotifyLoc]; * Do naked notify
CALLEXTERNAL[IOWrRegALoc], T ← (IOResetXmtrCRC);
GOTO [IONeedChar], PStore1[IOiocb, IOStatus, IOIOCBState!];

IODoTask: RETURN;
IOOutTask: NOP, GOTO [.-1];

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

:END[MIOCBit];