% File: MIOCTTY.mc

Edit by Danielson on March 19, 1981 1:25 PM:
Fix base reg fixup code.
Edit by Danielson on August 1, 1980 11:38 AM
Always leave status interrupts enabled.
Edit by Danielson on July 24, 1980 1:40 PM
Enabling/disabling transmitter.
Edit by Danielson on June 5, 1980 3:34 PM
Fix data lost code
Edit by Danielson on May 21, 1980 4:09 PM
Contains Async character at a time RS232C code for MIOC board
IOCB type chaining, input frame timeout, single
buffer/IOCB, single buffer/frame

%

insert[MIOCDefs];

Title [MIOCTTY];


SET TASK [0];

ONPAGE [IOPage];

IMRESERVE[IOPage,260,120];

* Registers

RV[IOState,1];
RV[IONext,3]; RV[IODataTemp,3];

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

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

M@[IOSizeDisp,DISPATCH[#1,10,2]];
* Character size dispatch
SET [IOSizeLoc,ADD[LSHIFT[IOPage,10],220]];
* 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];

* Initialization code

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

* Start transmitter: Treat as transmitter empty interrupt

GOTO [IONeedChar], AT [IOStartXmtrLoc];

* Timeout occurred:
* If no input data, clear timeout flag.
* If input data, check timeout flag.
* If timeout flag cleared, set it.
* If timeout flag set, force from to end and clear timeout flag.

NOP, AT [IOTimeoutLoc];* Memory bypass NOP!!
LU ← (IOMisc) AND (IOMiscTTYChars);
SKIP [ALU#0], LU ← (IOMisc) AND (IOMiscTTYTimeout);
GOTOEXTERNAL [IOIntrDoneLoc], IOMisc ← (IOMisc) AND NOT (IOMiscTTYTimeout);
SKIP [ALU#0], IOMisc ← (IOMisc) AND NOT (IOMiscTTYTimeout);
GOTOEXTERNAL [IOIntrDoneLoc], IOMisc ← (IOMisc) OR (IOMiscTTYTimeout);
CALL [IODoTask], PFetch1 [IOcsb, IOiocb, IOcsbIn!];
GOTO [IODoneIOCB], IOMisc ← (IOMisc) OR (IOMiscInput);

* 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, complete the IOCB, and check for any more frames.

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

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

CALL [IODoTask], PFetch1[IOcsb, IOStatus, IOcsbStat!], AT [IOIntrLoc,12];
IOStatus ← (IOStatus) OR (IOStatEvent);
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: PFetch1[IOBufBase, IODataTemp], CALL [IODoTask];
DBLGOTO [IOISpace, IOOSpace, R ODD], LU ← IOMisc;

* No IOCB condition:
* On input, set data lost in CSB.
* On output, satisfy xmtr 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);
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: End the IOCB

IONoSpace: GOTO [IODoneIOCB];

* Room in buffer (input case).
* Stores a single byte in buffer.
* IOOffset contains offset into buffer in bytes.
* Also sets flags indicating the IOCB has characters
* and clears timeout.

IOISpace: IOMisc ← (IOMisc) OR (IOMiscTTYChars);
IOMisc ← (IOMisc) AND NOT (IOMiscTTYTimeout);
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], IODataTemp ← RHMASK[IODataTemp];
IOOEven: GOTO [IOOBoth], IODataTemp ← RSH[IODataTemp,10];
IOOBoth: PFetch1[IOcsb, IORegs, IOcsbRegs!];
CALLEXTERNAL[IOWrRegALoc], T ← 5C;
T ← 10C;* Enable tranmsitter
CALLEXTERNAL [IOWrRegALoc], T ← (RSH[IORegs,10]) OR (T);
CALLEXTERNAL [IOWrDataALoc], T ← IODataTemp;
IOMisc ← (IOMisc) OR (IOMiscEnabled);
IONewOffset: IOOffset ← (IOOffset) + 1;
IOSaveOffset: PStore1[IOiocb, IOOffset, IOIOCBOffset!];
LU ← (IOStatus) AND (IOTTYBadStatus);
SKIP [ALU#0], IOMisc ← (IOMisc) OR (IOMiscTTYStatEnd);
GOTOEXTERNAL [IOIntrDoneLoc], IOMisc ← (IOMisc) AND NOT (IOMiscTTYStatEnd);
GOTO [IODoneIOCB], PStore1[IOiocb, IOStatus, IOIOCBStat!];

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

IODoneIOCB: PFetch1[IOiocb, IONext, IOIOCBNext!];
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], PFetch1[IOcsb, IOInt1, IOcsbMask!]; * Do naked notify
IOState ← (ZERO) - 1;* Set processed
DBLGOTO [IODoneIn, IODoneOut, R ODD] , LU ← (IOMisc) AND (OR[IOMiscFake!, IOMiscTTYStatEnd!]C);

* Output done. Set State to completed and return to main microcode

IODoneOut: PStore1[IOiocb, IOState, IOIOCBState!];
CALL [IODoTask];
GOTOEXTERNAL [IOIntrDoneLoc];

* Input end..check if real or fake interrupt.
* If fake interrupt, it was caused by timeout, so branch to
* Interrupt Done code. If not fake interrupt, it was caused by
* input buffer filling up. Therefore, we still have to try and
* store the input character, so load up new IOCB address and
* branch back up into code to look for more buffer space.

IODoneIn: DBLGOTO [IOInFake, IOInReal, ALU#0], IOMisc ← (IOMisc) AND NOT (IOMiscTTYChars);
IOInFake: GOTO [IODoneOut], IOMisc ← (IOMisc) AND NOT (IOMiscTTYStatEnd);
IOInReal: T ← IONext;
CALL [IODoTask], PStore1[IOiocb, IOState, IOIOCBState!];
GOTO [IOLoadIOCB], IOiocb ← T;

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

:END[MIOCTTY];