; AltoEIA2.mu -- Microcode for controlling the Alto EIA interface. ; This file contains the character-at-a-time and uninterpreted ; block software interfaces and the interval timer handler. ; Last modified September 23, 1978 4:16 PM ; **** Character-at-a-time Receiver **** ; LCB format: ; word 0 to 3 standard. ***Next LCB link must point to itself*** ; 4 begin pointer (first word of buffer) ; 5 end pointer (begin+length -- first word beyond end) ; 6 read pointer (next word to read) ; 7 write pointer (next word to write) ; Ring buffer conventions: ; Empty buffer is represented by write pointer = read pointer. ; Full buffer is represented by write pointer +1 = read pointer ; (modulo wraparound). ; These conventions are the ones used in the Bcpl Ring Buffer package ; (word version). ; In each word, the data byte is stored in the right half and the ; rightmost 8 bits of hardware status in the left half. !1, 2, ~RWrap, RWrap; !1, 2, ~RBOvf, RBOvf; RCAAT: T_ EIASTS; Get hardware status L_ 377 AND T; Keep just right byte MTEMP_ L LCY 8; Swap to left half T_ LCB; MAR_ 6+T; Reference LCB word 6 T_ EIADAT; Merge status with data byte L_ MTEMP OR T; EIADAT_ L; L_ MD; L_ read ptr (word 6) T_ MD; T_ write ptr (word 7) EIATMP_ L, MAR_ T; Reference word pointed to by write ptr L_ 1+T; L_ write ptr +1 MD_ EIADAT, TASK; Store status,,data in ring buffer EIADAT_ L; Save write ptr across TASK T_ LCB; MAR_ 5+T; Reference LCB word 5 L_ 7+T; Compute LCB+7 MTEMP_ L; T_ EIADAT; The saved write ptr +1 L_ MD-T; Compare with end ptr (word 5) L_ MD, SH=0; L_ begin ptr (word 4) T_ EIATMP, :~RWrap; [~RWrap, RWrap] T_ read ptr RWrap: EIADAT_ L; Wrap write ptr around to beginning ~RWrap: L_ EIADAT-T; Compare new write ptr with read ptr MAR_ MTEMP, SH=0; Reference LCB word 7 TASK, :~RBOvf; [~RBOvf, RBOvf] ~RBOvf: MD_ EIADAT, :ESRet2; Store write ptr, go generate interrupt ; Here if ring buffer is full. Post an overflow error. RBOvf: NOP; TASK pending L_ T_ 2, :EIAPost; Post code = 2, return index = 2 EPRet2: :SameState; ; **** Character-at-a-time Transmitter **** ; LCB format: ; word 0 to 3 standard ; 4 begin pointer (first word of buffer) ; 5 end pointer (begin+length -- first word beyond end) ; 6 read pointer (next word to read) ; 7 write pointer (next word to write) ; Ring buffer conventions: ; Empty buffer is represented by write pointer = read pointer. ; Full buffer is represented by write pointer +1 = read pointer ; (modulo wraparound). ; These conventions are the ones used in the Bcpl Ring Buffer package ; (word version). !1, 2, ~TRBE, TRBE; !1, 2, ~TWrap, TWrap; TCAAT: T_ LCB; MAR_ L_ 6+T; Reference LCB word 6 EIATMP_ L; T_ MD; T_ read ptr (word 6) L_ MD-T; Compare with write ptr (word 7) L_ T, SH=0, TASK; EIADAT_ L, :~TRBE; [~TRBE, TRBE] EIADAT_ read ptr ; Transmit ring buffer not empty. Update read pointer. ~TRBE: MAR_ EIATMP-1; Reference LCB word 5 T_ EIADAT+1; The saved read ptr +1 L_ MD-T; Compare with end ptr (word 5) L_ MD, SH=0; L_ begin ptr (word 4) MAR_ EIATMP, :~TWrap; [~TWrap, TWrap] Reference LCB word 6 ~TWrap: L_ T; Not wraparound, use old read ptr +1 TWrap: TASK; If at end, use begin ptr MD_ M; Update read ptr ; Transmit the data byte MAR_ EIADAT; Fetch word pointed to by old read ptr T_ 2; EIASend return index 2 L_ MD, :EIASend; L_ data byte, go send it ; Now generate interrupt if desired by software. ESRet2: MAR_ LCB+1; Reference LCB word 1 T_ NWW; Current wakeups waiting L_ MD OR T, TASK; Merge in word 1 (interrupt mask) NWW_ L, :SameState; Stay in same state ; If ring buffer is empty, do nothing. TRBE: :SameState; ; **** Uninterpreted Block Transmitter **** ; 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 control command to be issued at end of block ; Bytes are fetched two per word, left byte first. !1, 2, OkLCB5, NoLCB5; !1, 2, TUMore, TUDone; !1, 2, URight, ULeft; ; "Idle" state. ; When awoken, see if there is an LCB, and if so start sending data. TUIdl: T_ LCB, BUS=0; Is there an LCB? MAR_ 2+T, :OkLCB5; [OkLCB5, NoLCB5] Reference LCB word 2 ; Here when there is no LCB. Just ignore the wakeup. NoLCB5: :SameState; ; There is an LCB. Zero the microcode status, which may have had an ; error posted due to Fill Character Transmitted hardware status. OkLCB5: L_ TUBlk#; L_ new state to enter immediately MD_ 0, :ChangeStateGo; Store zero in LCB word 2 ; ChangeStateGo immediately returns control to TUBlk to transmit first byte. ; "Transmitting uninterpreted block" state. ; Transmit next data byte if there is one. TUBlk: T_ 4; MAR_ L_ LCB+T; Reference LCB word 4 L_ M+1; L_ LCB+5 MTEMP_ L; 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, :TUMore; [TUMore, TUDone] EIATMP_ address ; Count wasn't zero, send another data byte. TUMore: L_ 77777+T; Decrement count and flip bit 0 MAR_ MTEMP-1; Reference LCB word 4 T_ EIATMP+1, SH<0; T_ address+1, which byte? MD_ M, L_ T, TASK, :URight; [URight, ULeft] Word 4 _ updated count ; Left byte is next. ULeft: 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; Swap to right L_ MTEMP, :UData1; L_ data byte, go send ; Right byte is next. URight: MD_ M; Word 5 _ address+1 MAR_ EIATMP; Reference data word in buffer T_ 377; Extract right byte L_ MD AND T; ; Now send data byte. L = data byte. UData1: T_ 3, :EIASend; EIASend return index 3 ESRet3: :SameState; Remain in same state ; Here when entire block has been transmitted. ; Issue control command specified in LCB, if any. TUDone: MAR_ MTEMP+1; Reference LCB word 6 NOP; L_ MD; L_ control command MAR_ 177700; Reference control register TASK; MD_ M; Issue command ; Post completion and chain to next block, if any. L_ 0; L_ post code 0 T_ 3, :EIAPost; T_ return index 3 EPRet3: L_ TUIdl#, :ChangeStateGo; Enter idle state immediately