% 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).(0,4032)(1,13760) * 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];