; CommProc1.mu -- Microcode for controlling the Alto Communication Processor. ; This file contains the main dispatch, subroutines, ; and BiSync software interface. ; Last modified September 23, 1978 3:54 PM ; Data structures: ; LINTAB is an 8-entry table (one per line). Each entry is 6 words, ; and its base is at LINTAB + 6*line, which must be even. ; The layout of an entry is: ; word 0 unused ; 1 current hardware status, posted by microcode on every wakeup ; 2 pointer to input Line Command Block (LCB) ; 3 current input state ; 4 pointer to output LCB ; 5 current output state ; The word at LINTAB-1 must contain a pointer to a 256-word table ; that assists in CRC computation. See comments after UpdateCRC ; for information on its construction. ; The words at LINTAB-4 through LINTAB-2 must be set up as required by ; the Interval Timer Task, if the hardware Interval Timer is being used. ; See comments above the IntTimerTask code. ; The line state is a small integer representing the state of the microcode ; with respect to a given line and direction. It is used to dispatch to ; the appropriate microcode after initial, common processing is completed. ; The microcode updates this word to change states. ; LCBs must be aligned on even-word boundaries. ; The first four words of the LCB have a standard interpretation: ; word 0 link to next LCB (0 if none) ; 1 interrupt bit mask ; 2 microcode status is posted here upon completion: ; bit 0 = 0 before completion, set to 1 upon completion ; (must be initialized to zero by software). ; bits 1-15 are error status: ; 0 no error ; 1 fatal error status ; >1 protocol-dependent error ; 3 hardware status is posted here upon completion or error ; Registers dedicated to CommProc microcode. ; They may be R- or S-registers. $LINTAB $R71; Address of base of line table $LTEPTR $R72; Address of LINTAB entry for current line $LCB $R73; Address of LCB for current line $LINE*4 $R77; Current line number in B10:13 (for addressing control regs). ; *** Must never use LINE*4 in instruction with MAR←. $EIADAT $R74; EIA data, temporary $EIATMP $R75; Temporary ; Standard R-registers $NWW $R4; $MTEMP $R25; ; CommProc task-specific functions ; F1=16: BUS[11] ← 1 iff input request; BUS[12:15] ← line number. ; *** Must define this as a non-data F1 due to botch in Mu that ; causes data F1's to set the BS field as well. $COMLINE $L 016016, 000000, 000100; ; F1=14: if input request, BUS[0:7] and BUS[8:15] ← input data (same value ; in both bytes); if output request, output data ← BUS[0:7]. $COMDATA $L 020014, 070014, 120100; ; State number assignments. ; Must be parallel to EIAStates predefinition. ; States 0-7 are reserved as initial states. ; Note: the Alto constant memory has small integers only up to 31. ; Larger state numbers will have to be manufactured on the fly. ; Throwaway state -- all wakeups are ignored $Reset# $0; ; States for BiSync receiver $RIdle# $1; Idle, waiting for first DLE -- Initial state. $RDLE1# $10; Received first DLE, waiting for STX $RData# $11; Receiving data, in transparent mode $RDLE2# $12; Received DLE during data, waiting for next byte $RETX1# $13; Received ETX, waiting for first CRC byte $RETX2# $14; Received first CRC byte, waiting for second ; States for BiSync transmitter $TIdle# $2; Idle, waiting for something to send -- Initial state. $TSYN1# $15; Sent first SYN $TSYN2# $16; Sent second SYN $TSYN3# $17; Sent third SYN, waiting to send DLE $TDLE1# $20; Sent DLE, waiting to send STX $TData# $21; Transmitting data, in transparent mode $TDLE2# $22; Sent DLE, waiting to send second one (DLE doubling) $TDLE3# $23; Sent DLE, waiting to send ETX $TETX1# $24; Sent ETX, waiting to send first CRC byte $TETX2# $25; Sent first CRC byte, waiting to send second $TEnd# $26; Sent second CRC byte, ready to post completion ; State for character-at-a-time receiver $RCAAT# $3; ; State for character-at-a-time transmitter $TCAAT# $4; ; States for uninterpreted block mode transmitter $TUIdl# $5; Idle, waiting for something to send -- Initial state $TUBlk# $27; Transmitting data block !37,40, Discard, RIdle, TIdle, RCAAT, TCAAT, TUIdl, , , RDLE1, RData, RDLE2, RETX1, RETX2, TSYN1, TSYN2, TSYN3, TDLE1, TData, TDLE2, TDLE3, TETX1, TETX2, TEnd, TUBlk; ; Random constants $1 $1; $170000 $170000; $177700 $177700; $177701 $177701; ; Predefinitions !1, 2, CPIn, CPOut; !1, 2, SameState, LIMCon0; !3, 4, LCRet0, LCRet1, LCRet2, LCRetX; Extra return reserved for external caller $LCRetX# $3; Constant corresponding to LCRetX offset ; *** CommProc Task *** ; Reset location of the task. ; Upon wakeup, determine line number and direction of request. CommProcTask: T← 17-1, COMLINE; T← line-1 L← T← 17+T+1, COMLINE; L← T← 2*line L← M+T; L← 4*line LINE*4← L; Save for addressing control regs L← 20, COMLINE; Test in/out bit T← LINE*4+T+1, SH=0; T← 6*line +1 L← T← LINTAB+T+1, :CPIn; [CPIn, CPOut] L← T← LINTAB + 6*line +2 ; Here if input request. ; Read the data byte and store it in EIADAT. CPIn: LTEPTR← L, MAR← T; Reference word 2 of entry, save ptr T← 377; Data mask L← COMDATA AND T; Get the data byte EIADAT← L, :CPCom; ; Here if output request. CPOut: MAR← L← 2+T; Reference word 4 of entry, save ptr LTEPTR← L; ; Note that we normally dispatch without testing to see whether there is ; an LCB. The assumption is that if there is no LCB, the microcode will ; have left the line in the Idle state or will otherwise be prepared ; to handle this condition. CPCom: L← MD; LCB SINK← MD, BUS, TASK; Dispatch on line state LCB← L, :Discard; [Discard, ...] ; Here if in discard state. ; If this is an input wakeup, just discard the received character. ; If an output wakeup, turn off Send Enable. Discard: L← 20, COMLINE; Test in/out bit (1 = input) T← 100, SH=0; Send Enable bit L← 0, :SameState; [SameState, LIMCon0] LIMCon0 return 0 LCRet0: BLOCK, :SameS1; ; *** Task exit code *** ; Branch to ChangeState with new state number in L, or ; to SameState to leave the state unchanged. ChangeState: MAR← LTEPTR+1; Store into state word BLOCK; Clear wakeup TASK; MD← M, :CommProcTask; ; Here when done processing and don't want to change state. SameState: BLOCK; SameS1: TASK; :CommProcTask; ; Branch to ChangeStateGo with new state number in L, to establish ; a new state and immediately dispatch to the microcode for that state. ChangeStateGo: MAR← LTEPTR+1; Store into line state word SINK← M, BUS, TASK; Dispatch on new state MD← M, :Discard; [Discard, ...] ; *** EIA subroutines *** ; Subroutine to post LCB completion. ; T has return index, L has post code ; Posts the supplied code and current hardware status and marks LCB completed. ; Returns having set up new LCB, if any. ; Does nothing if there is no LCB. ; Clobbers EIATMP. !3, 4, EPRet0, EPRet1, EPRet2, EPRet3; !1, 2, OkLCB1, NoLCB1; !1, 2, EPIn, EPOut; !1, 2, EPErr, ~EPErr; EIAPost: MTEMP← L, L← T; MTEMP← post code, L← return index SINK← LCB, BUS=0; Test for no LCB present EIATMP← L, :OkLCB1; [OkLCB1, NoLCB1] EIATMP← return index OkLCB1: T← 177000; Construct status register address T← 300 OR T; T← 177300 T← LINE*4+T+1; T← 177301 + 4*line L← 20, COMLINE; Test in/out bit MAR← 2+T, T← 2; MAR← 177303 + 4*line (status for line) L← LCB+T, SH=0; LCB← pointer to LCB word 2 LCB← L, :EPIn; [EPIn, EPOut] Select error mask: EPOut: T← 10, :EPCom1; Output: Overrun(T) EPIn: T← 7; Input: Overrun(R), Parity, Framing EPCom1: L← MD AND T, T← MD; L← errors, T← entire status word L← T, T← 100000, SH=0; L← status MAR← LCB+1, :EPErr; [EPErr, ~EPErr] Reference LCB word 3 ~EPErr: T← MTEMP OR T, :EPCom2; No errors, set to post supplied code EPErr: T← 1 OR T; Errors, set to post code 1 EPCom2: MD← M, L← T, TASK; Word 3 ← hardware status MD← M; Word 2 ← post code ; Cause interrupt as specified in LCB's mask, then chain to next LCB MAR← LCB-1; Reference LCB word 1 T← NWW; Current wakeups waiting L← MD OR T; Merge in word 1 (mask) T← MD; T← word 0 (new LCB) MAR← LTEPTR; Reference LCB pointer in LINTAB entry NWW← L, L← T; Update wakeups MD← M; Store new LCB pointer in memory EPExit: SINK← EIATMP, BUS, TASK; Dispatch on return index LCB← L, :EPRet0; [EPRet0, ...] ; Here if there was no LCB. NoLCB1: L← 0, :EPExit; Exit having done nothing ; Subroutine to send data byte on current line. ; T has return index, L has data byte. !3, 4, ESRet0, ESRet1, ESRet2, ESRet3; EIASend: MTEMP← L LCY 8, L← T; MTEMP← data byte, L← return index SINK← M, BUS, TASK; Dispatch on return index COMDATA← MTEMP, :ESRet0; [ESRet0, ...] BUS[0:7] ← data byte ; Subroutine to test data byte in EIADAT against one or more constants. ; The control structure is somewhat tricky, and is best explained ; by the following example: ; L← 0, BUS, :BTInit; ; BTne0: T← first constant, :BTest; ; BTne1: T← second constant, :BTest; ; BTne2: T← third constant, :BTest; ; BTne3: ; Get here if EIADAT matched none of the constants ; BTeq1: ; Get here if EIADAT matched the first constant ; BTeq2: ; Get here if EIADAT matched the second constant ; BTeq3: ; Get here if EIADAT matched the third constant ; The flow of control is as follows. The first instruction established ; an initial dispatch index (0 in this case), and BTInit returns to ; the 'BTneX' label corresponding to this index. Then, for successively ; increasing values of X, BTest compares the constant passed to it against ; EIADAT and returns to 'BTneX' if they don't match and 'BTeqX' if they do. ; A 'TASK' is done shortly before the return to either label. ; EIATMP is clobbered. ; The 'BTneX' and 'BTeqX' labels are given in the following predefinitions, ; which must be parallel and in strictly sequential values of X. ; Note that there need not be a 'BTeqX' label for values of X that ; are initial dispatch indices (except BTeq0, which is a dummy). !17, 20, BTne0, BTne1, BTne2, BTne3, BTne4, BTne5, BTne6, BTne7, BTne10, BTne11, BTne12; !17, 20, BTeq0, BTeq1, BTeq2, , BTeq4, , BTeq6, BTeq7, BTeq10, , BTeq12; !1, 2, BTInit, BTeq; BTest: L← EIADAT-T; Test data byte equal to argument L← EIATMP+1, SH=0; Increment index and dispatch on it BTeq0: SINK← M, BUS, TASK, :BTInit; [BTInit, BTeq] BTInit: EIATMP← L, :BTne0; [BTne0, ...] Not equal BTeq: EIATMP← L, :BTeq0; [BTeq0, ...] Equal ; Subroutines to set or reset bits in LIM control register. ; L has return index, T has mask of bits to set or reset (B[4:15] only). ; Clobbers EIATMP. ; LCRetX predefinition appears earlier. !1, 2, LIMC0, LIMC1; ; Call here to set masked bit(s) to one: LIMCon1: T← 100000 OR T; Remember setting bits to one ; Call here to set masked bit(s) to zero: LIMCon0: MTEMP← L, L← T; MTEMP← return index, L← mask T← 177000; Construct control register address T← LINE*4 OR T; = 177300 + 4*line MAR← T← 300 OR T; Fetch current value T← M, L← T, SH<0; T← mask, L← address, test 0/1 call EIATMP← L, :LIMC0; [LIMC0, LIMC1] EIATMP← address LIMC0: L← MD AND NOT T, :LIMCx; Setting masked bit(s) to zero LIMC1: L← MD OR T; Setting masked bit(s) to one LIMCx: MAR← EIATMP; Store updated control word SINK← MTEMP, BUS, TASK; Dispatch on return index MD← M, :LCRet0; [LCRet0, ...] ; Subroutine to update CRC in LCB given new data byte in EIADAT. ; L has return index. ; Also returns updated CRC in EIADAT. ; Does nothing harmful if there is no LCB. ; Clobbers EIATMP. ; Altorithm is: ; CRC = (CRC rshift 8) xor CRCTab!((CRC xor data) & #377) !7, 10, UCRet0, UCRet1, UCRet2, UCRet3, UCRet4, UCRet5, UCRet6; !1, 2, OkLCB2, NoLCB2; UpdateCRC: T← 6; MAR← LCB+T, BUS=0; Reference LCB word 6 EIATMP← L, :OkLCB2; [OkLCB2, NoLCB2] EIATMP← return index OkLCB2: T← EIADAT; The data byte L← MD XOR T, T← MD; L← CRC table index, T← old CRC MAR← LINTAB-1; LINTAB-1 points to CRC table T← M, L← T; T← index, L← old CRC MTEMP← L LCY 8; MTEMP← old CRC lcy 8 T← 377 . T; Zero high byte of table index L← MD+T; L← desired address in CRC table MAR← M; Fetch word from CRC table T← 377; Compute old CRC rshift 8 T← MTEMP . T; L← MD XOR T, TASK; Compute new CRC EIADAT← L; Store here temporarily T← 6; MAR← LCB+T; Reference LCB word 6 SINK← EIATMP, BUS, TASK; Dispatch on return index MD← EIADAT, :UCRet0; [UCRet0, ...] Store updated CRC ; Here if there is no LCB. Return having done nothing. NoLCB2: SINK← EIATMP, BUS, TASK; :UCRet0; [UCRet0, ...] ; Note: ; LINTAB-1 must contain a pointer to a 256-word table CRCTab, ; constructed as follows: ; for i = 0 to 255 do ; [ ; let crc = 0 ; let val = i ; for power = 0 to 7 do ; test (val & 1) eq 0 ; ifso ; val = val rshift 1 ; ifnot ; [ ; crc = crc xor (#120001 rshift (7 - power)) ; val = (val rshift 1) xor #120001 ; ] ; CRCTab!i = crc ; ] ; **** BiSync Receiver finite state machine **** ; This FSM runs whether or not there is an LCB describing a buffer ; to be read into. Code that references the LCB tests carefully for ; its nonexistence. Thus, we stay in sync with incoming data even ; if the software fails to provide an LCB. ; BiSync character constants $SYN $26; $DLE $20; $STX $2; $ETX $203; ; LCB format: ; word 0 to 3 standard ; 4 bit 0: 0 if left byte is next, 1 if right ; bits 1-15: remaining byte count ; 5 address of word containing next byte ; 6 partial CRC (must be zeroed initially by software) !1, 2, ~RDDLE, RDDLE; !1, 2, OkLCB3, NoLCB3; !1, 2, ~RDOvf, RDOvf; !1, 2, RRight, RLeft; !1, 2, CRCEr, RPost; ; "Idle" state. ; If new byte is: ; SYN ignore, stay in idle state ; DLE ignore, expect STX next ; other drop sync, restart receiver, stay in Idle state RIdle: L← 0, BUS, :BTInit; Supply initial dispatch to BTest BTne0: T← SYN, :BTest; Is it a SYN? BTne1: T← DLE, :BTest; No, is it a DLE? BTne2: :EPRet1; No, restart receiver ; SYN -- ignore, stay in idle state. BTeq1: :SameState; ; DLE BTeq2: L← RDLE1#, :ChangeState; Change state, wait for next data byte ; "Received first DLE" state. ; If new byte is: ; STX enter Receive Data state ; other post line control error in LCB and restart receiver RDLE1: L← 3, BUS, :BTInit; Supply initial dispatch to BTest BTne3: T← STX, :BTest; Is it an STX? BTne4: L← 3, :RPost; No, post protocol error ; STX -- enter Receive Data State BTeq4: L← RData#, :ChangeState; Change state, wait for next data byte ; BiSync Receiver FSM (cont'd) ; "Receive Data" state -- main transfer loop. ; Unless byte is DLE, store it in the buffer and update pointer and count. RData: T← EIADAT; Received data byte L← DLE-T; Is it a DLE? T← 4, SH=0; RData0: MAR← L← LCB+T, BUS=0, :~RDDLE; [~RDDLE, RDDLE] Reference LCB word 4 ~RDDLE: MTEMP← L, :OkLCB3; [OkLCB3, NoLCB3] MTEMP← LCB+4 OkLCB3: T← 77777; L← MD AND T, T← MD; T← LCB word 4 (count), test count=0 L← MD, SH=0; L← LCB word 5 (address) EIATMP← L, :~RDOvf; [~RDOvf, RDOvf] EIATMP← address ; Count wasn't zero, so buffer didn't overflow ~RDOvf: L← 77777+T; Decrement count and flip bit 0 MAR← MTEMP; Reference LCB word 4 T← EIATMP+1, SH<0; T← address+1, which byte? MD← M, L← T, TASK, :RRight; [RRight, RLeft] Word 4 ← updated count ; Left byte is next. RLeft: MD← EIATMP; Word 5 ← address (unchanged) L← EIADAT; The received data byte MAR← EIATMP; Reference data word in buffer MTEMP← L LCY 8; Swap data into left byte TASK, :RData1; Go store in buffer ; Right byte is next. RRight: MD← M; Word 5 ← address+1 MAR← EIATMP; Reference data word in buffer T← EIADAT; The received data byte L← MD OR T; Merge with existing left byte MAR← EIATMP; Store the word back in buffer MTEMP← L, TASK; RData1: MD← MTEMP; Complete the store ; Update CRC and do not change state. NoLCB3: L← 0, :UpdateCRC; UpdateCRC return index 0 UCRet0: :SameState; ; Here if buffer overflowed. ; Assume that the reason for buffer overflow is that the receiver has ; lost character sync, causing it to miss the end-of-packet (DLE ETX). ; Post LCB completion with overflow error, then restart receiver. RDOvf: L← 4, :RPost; ; Here if DLE received. RDDLE: L← RDLE2#, :ChangeState; [ChangeState] wait for next data byte ; BiSync Receiver FSM (cont'd) ; "DLE during data" state. ; If byte after DLE is: ; DLE put one DLE in the buffer and treat it as data ; SYN ignore ; ETX include in CRC and await ending CRC bytes RDLE2: L← 5, BUS, :BTInit; Supply initial dispatch to BTest BTne5: T← DLE, :BTest; Is it a DLE? BTne6: T← SYN, :BTest; No, is it a SYN? BTne7: T← ETX, :BTest; No, is it an ETX? ; Not any of the legal successors to DLE. Post LCB completion with error. BTne10: L← 5, :RPost; ; DLE DLE -- treat as single DLE data byte BTeq6: MAR← LTEPTR+1; Change state back to "Receive Data" TASK; MD← RData#; T←4, :RData0; Go process DLE as normal data byte ; DLE SYN -- ignore BTeq7: L← RData#, :ChangeState; ; DLE ETX -- include ETX in CRC and await CRC bytes BTeq10: L← 1, :UpdateCRC; UpdateCRC return index 1 UCRet1: L← RETX1#, :ChangeState; ; First "Received ETX" state. ; Data byte is first CRC byte -- include in CRC and await second. RETX1: L← 2, :UpdateCRC; UpdateCRC return index 2 UCRet2: L← RETX2#, :ChangeState; ; Second "Received ETX" state. ; Data byte is second CRC byte -- include in CRC. ; Then check computed CRC and post LCB completion appropriately. RETX2: L← 3, :UpdateCRC; UpdateCRC return index 3 UCRet3: L← EIADAT, BUS=0; Test computed CRC for zero :CRCEr; [CRCEr, RPost] ; CRC error, post error code. CRCEr: L← 6, :RPost; ; Here to post LCB completion. L has post code. RPost: T← 1, :EIAPost; EIAPost return index 1 ; Restart receiver searching for SYN. EPRet1: T← 200; Turn Receive Enable off L← 1, :LIMCon0; LIMCon0 return index 1 LCRet1: T← 200; Now turn Receive Enable back on L← 2, :LIMCon1; LIMCon1 return index 2 LCRet2: L← RIdle#, :ChangeState; Revert to idle state ; **** BiSync Transmitter finite state machine **** ; LCB format: ; word 0 to 3 standard ; 4 bit 0: 0 if left byte is next, 1 if right ; bits 1-15: remaining byte count ; 5 address of word containing next byte ; 6 partial CRC (must be zeroed initially by software) !1, 2, OkLCB4, NoLCB4; !1, 2, ~TDone, TDone; !1, 2, TRight, TLeft; !1, 2, TCRC1, TCRC2; ; "Idle" state and states for sending initial SYN SYN SYN DLE STX. ; When awoken, start sending a bunch of SYNs (3 of them). ; If we are awoken from the Idle state with no LCB, just ignore the wakeup ; and stay in the same state. TIdle: T← LCB, BUS=0; Is there an LCB? :OkLCB4; [OkLCB4, NoLCB4] ; There is an LCB. Begin transmission of a new frame. OkLCB4: L← TSYN1#, :SndSYN; Send SYN and enter new state ; Here if awoken from Idle state with no LCB. NoLCB4: :Discard; Reset Send Enable, stay in Idle state ; Send some more SYNs TSYN1: L← TSYN2#, :SndSYN; Send second SYN TSYN2: L← TSYN3#, :SndSYN; Send third SYN ; Finished sending a bunch of SYNs. Now send DLE STX. TSYN3: L← TDLE1#, :SndDLE; Send DLE and enter new state TDLE1: T← STX; Send STX and enter new state L← TData#, :SndChg; ; BiSync Transmitter FSM (cont'd) ; "Transmit Data" state -- main transfer loop. TData: T← 4; MAR← L← LCB+T; Reference LCB word 4 MTEMP← L; MTEMP← LCB+4 T← 77777; L← MD AND T, T← MD; T← LCB word 4 (count), test count=0 L← MD, SH=0; L← LCB word 5 (address) EIATMP← L, :~TDone; [~TDone, TDone] EIATMP← address ; Count wasn't zero, send another data byte. ~TDone: L← 77777+T; Decrement count and flip bit 0 MAR← MTEMP; Reference LCB word 4 T← EIATMP+1, SH<0; T← address+1, which byte? MD← M, L← T, TASK, :TRight; [TRight, TLeft] Word 4 ← updated count ; Left byte is next. TLeft: MD← EIATMP; Word 5 ← address (unchanged) MAR← EIATMP; Reference data word in buffer T← 177400; Extract left byte L← MD AND T; MTEMP← L LCY 8, T← 0+1; Swap to right, T← 1 (EIASend index) L← MTEMP, :TData1; L← data byte, go send ; Right byte is next. TRight: MD← M; Word 5 ← address+1 MAR← EIATMP; Reference data word in buffer T← 377; Extract right byte L← MD AND T; T← 1; EIASend return index 1 ; Now send data byte. L = data byte, T = 1. TData1: EIADAT← L, :EIASend; EIADAT← data byte, go send it ; Test whether it is a DLE. ESRet1: L← 11, BUS, :BTInit; Supply initial dispatch to BTest BTne11: T← DLE, :BTest; Is it a DLE? ; Not a DLE. Update CRC and do not change state. BTne12: L← 4, :UpdateCRC; UpdateCRC return index 4 UCRet4: :SameState; ; Transmitted a DLE. Update CRC with this one, then send another ; DLE but do not include that one in the CRC. BTeq12: L← 5, :UpdateCRC; UpdateCRC return index 5 UCRet5: L← TDLE2#, :ChangeState; ; Send second DLE and change state back to "Transmit Data". TDLE2: L← TData#, :SndDLE; ; BiSync Transmitter FSM (cont'd) ; Here when all data has been transmitted. ; Now send DLE. TDone: L← TDLE3#, :SndDLE; ; Now send ETX and include it in the CRC. TDLE3: L← ETX; Set up ETX byte for UpdateCRC EIADAT← L; L← 6, :UpdateCRC; UpdateCRC return index 6 UCRet6: T← ETX; Send ETX and change state L← TETX1#, :SndChg; ; Send the CRC, low-order byte first, then high. TETX1: L← 377, :TCRC; Mask for low byte TETX2: L← 177400, :TCRC; Mask for high byte ; Common code for the TETX1 and TETX2 states TCRC: T← 6; Reference LCB word 6 = CRC MAR← LCB+T; T← M, SH<0; Which half? L← T← MD . T, :TCRC1; [TCRC1, TCRC2] Mask the byte ; Here to send the low-order byte. TCRC1: L← TETX2#, :SndChg; Send byte, change state ; Here to send the high-order byte. TCRC2: MTEMP← L LCY 8; Swap into right byte T← MTEMP; L← TEnd#, :SndChg; Send byte, change state ; Here when the last CRC byte has been sent. ; Post LCB completion, send pad character, and revert to idle state. ; When the next wakeup occurs, if another LCB hasn't been set up yet, ; the code at TIdle will reset Send Enable to turn off the transmitter. TEnd: L← T← 0, :EIAPost; Return index 0, post code 0 EPRet0: T← 377; Send all-ones pad character L← TIdle#, :SndChg; Change to Idle state ; Here to send DLE and change to state given in L. SndDLE: T← DLE, :SndChg; ; Here to send SYN and change to state given in L. SndSYN: T← SYN, :SndChg; ; Here to send data byte in T and change to state given in L. SndChg: EIATMP← L, L← T; Save new state T← 0, :EIASend; EIASend return index 0 ESRet0: L← EIATMP, :ChangeState; Restore state, change to it