; 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