% File: MIOCByte.mc

Edit by Danielson on March 19, 1981 1:25 PM:
Fix base reg fixup code.
Edit by Danielson on August 1, 1980 11:33 AM:
Leave external status interrupts enabled.
Edit by Danielson on July 25, 1980 1:34 PM:
New enabling/disabling code for transmitter.
Edit by Danielson on June 5, 1980 5:06 PM:
Fix data lost code
Edit by Danielson on April 18, 1980 3:40 PM:
Added intra frame fill code

Contains Byte Synchronous RS232C code for MIOC board
1. IOCB type chaining
2. State table for BISYNC type frames
3. single buffer/IOCB, single buffer/frame
4. Timeouts for intraframe fill.
%

insert[MIOCDefs];

Title [MIOCByte];

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 interrupts 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: Fake a transmitter interrupt.

GOTO [IOXmtrStart], AT [IOStartXmtrLoc];

* Timeout occurred: Decide if a fill character needs to be sent.
* This is done by examining the "timeout occurred" flag. It
* indicates that a single timeout has occurred without seeing
* a SYN character. If the "timeout occurred" flag is not set,
* this is the first timeout, and the flag is set. If the "timeout
* occurred" flag is set, the "fill needed" flag is set, telling
* the main microcode loop that a fill sequence (either
* SYN SYN or DLE SYN) should be sent as soon as possible.

NOP, AT [IOTimeoutLoc];* Memory bypass NOP!!
LU ← (IOMisc) AND (IOMiscTimeout);
SKIP [ALU#0], IOMisc ← (IOMisc) AND NOT (IOMiscTimeout);
GOTOEXTERNAL [IOIntrDoneLoc], (IOMisc) ← (IOMisc) OR (IOMiscTimeout);
GOTOEXTERNAL [IOIntrDoneLoc], (IOMisc) ← (IOMisc) OR (IOMiscFill);

* 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: PFetch1[IOcsb, IOiocb, IOcsbOut!], CALL [IODoTask], AT [IOIntrLoc,10];
IOMisc ← (IOMisc) AND NOT (IOMiscInput);
GOTO [IODoIt], IOEvents ← OR[IOEventBit[2],IOEventBit[3]]C;

* Interrupt vector = 5 (External status change). Flag status
* event in CSB and then reset external 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!];
GOTOEXTERNAL [IOIntrDoneLoc];

* Interrupt vector = 7. (Special received condition). Save status
* for the Mesa face and then treat as though a good character had
* been received.

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;
PFetch1[IOcsb, IOiocb, IOcsbIn!], CALL [IODoTask];
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.
* If IOMiscFill or IOMiscSentFirst is set, we should send fill sequence.
* Fill character sequences are located at offsets 2300-2325B in tables.
* Each word in the tables corresponds to an state. The 16
* bits contain two bytes to send.
* The code fetches the correct entry from the tables and then
* checks the "sentFirst" flag. If this flag is not set, it
* indicates that we should send the first byte and then set the
* flag so that the next time the second byte will be sent. If the
* "sendFirst" flag is set, we send the second byte and then clear
* it and IOMiscFill to indicate that the fill sequence has been sent.
* In either case, the value of IOEvents is changed
* so that is will only perform the "backup", "send character",
* and "save state" events (Event bits 1, 6, and 13).

SET[FillEventBits,LSHIFT[OR[IOEventBit[1],IOEventBit[6],IOEventBit[13]],1]];

LU ← (IOMisc) AND (OR[IOMiscFill!,IOMiscSentFirst!]C), AT [IOActionLoc,0];
SKIP [ALU#0], IODataTemp ← 2000C;
GOTO [IODoNext];* Fill not needed
PFetch1[IOiocb, IOState, IOIOCBState!];
TASK, IOEvents ← AND[FillEventBits,177400]C;* Set hi byte of events 1, 6, and 13
T ← (IODataTemp) + (SUB[300,20]C);* FillCharTable starts at offset 2300
CALL [IODoTask], PFetch2[IOcsb, IOBufBase, IOcsbTables!];
CALL [IODoTask], T ← (RSH[IOState,10]) + (T);
CALL [IODoTask], PFetch1[IOBufBase, IODataByte];
LU ← (IOMisc) AND (IOMiscSentFirst);
GOTO [IOSendSecond, ALU#0], IOEvents ← (IOEvents) OR (AND[FillEventBits,377]C);
IOSendFirst: IODataByte ← (RSH[IODataByte,10]);
GOTO [IODoNext], IOMisc ← (IOMisc) OR (IOMiscSentFirst);
IOSendSecond: IODataByte ← (RHMASK[IODataByte]);
GOTO [IODoNext], IOMisc ← (IOMisc) AND NOT (OR[IOMiscSentFirst!, IOMiscFill!]C);

* 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];
IOSpace: PFetch1[IOBufBase, IODataTemp], CALL [IODoTask];
DBLGOTO [IOISpace, IOOSpace, R ODD], LU ← IOMisc;

* No IOCB condition:
* On input, reset SIO chip and if data lost not set, set data lost in CSB and notify head via naked notify.
* On output, satisfy SIO transmitter and disable transmitter.

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 SYNs
CALL [IODoTask], 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 and send FF character.

IONoSpace: DBLGOTO[IOINoSpace, IOONoSpace, R ODD], LU ← IOMisc;
IOINoSpace: CALL [IOSaveIOCBStat], T ← 40C;
* Rcvr overrun bit
GOTO [IODoneIOCB];
IOONoSpace: NOP;
CALLEXTERNAL [IOWrDataALoc], T ← 377C;
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];
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!];

* Event bit 5
* Zero the BCC

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

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

IOSendData: PFetch1[IOcsb, IORegs, IOcsbRegs!], AT [IOActionLoc,6];
CALLEXTERNAL [IOWrDataALoc], T ← IODataByte;
CALLEXTERNAL [IOWrRegALoc], T ← 5C;
T ← 10C;* Xmtr enable bit
CALLEXTERNAL [IOWrRegALoc], T ← (RSH[IORegs,10]) OR (T);
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
* SYN seen.
* SYNs are used to determine whether frames should be timed out
* (on input) or fill inserted (on output).
* On input: clear "timeout occurred" and "send fill" flags.
* On output: Set SynSeen bit in CSB.

DBLGOTO [IOClearIn, IOClearOut, R ODD], LU ← IOMisc, AT [IOActionLoc,11];
IOClearIn: CALL [IODoTask], PFetch1[IOcsb, IOStatus, IOcsbStat!];
CALL [IODoTask], IOStatus ← (IOStatus) OR (IOStatSynSeen);
GOTO [IODoNext], PStore1 [IOcsb, IOStatus, IOcsbStat!];
IOClearOut: GOTO [IODoNext], IOMisc ← (IOMisc) AND NOT (OR[IOMiscTimeout!, IOMiscFill!]C);

* Event bit 12
* End frame.
* Remove IOCB from chain and issue naked notify.
* If input IOCB, reset SIO chip to look for SYNs.

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[IODoneIn,IODoneOut, R ODD], LU ← IOMisc;* Update CSB
IODoneIn: PFetch1[IOcsb, IORegs, IOcsbRegs!];
CALLEXTERNAL[IOWrRegALoc], T ← 3C;* Write register 3
CALLEXTERNAL[IOWrRegALoc], T ← RHMASK[IORegs];* Set chip to look for SYNs
GOTO [IODoneBoth], Pstore1[IOcsb, IONext, IOcsbIn!];
IODoneOut: Pstore1[IOcsb, IONext, IOcsbOut!];
IODoneBoth: 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[MIOCByte];