; CommProc2.mu -- Microcode for controlling the Alto Communication Processor. ; This file contains the character-at-a-time and uninterpreted ; block software interfaces and the interval timer handler. ; Last modified September 23, 1978 3:54 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). !1, 2, ~RWrap, RWrap; !1, 2, ~RBOvf, RBOvf; RCAAT: T← LCB; MAR← 6+T; Reference LCB word 6 NOP; 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 data byte 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, turn off Send Enable. TRBE: :Discard; ; **** 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; !1, 2, TCCom, ~TCCom; ; "Idle" state. ; When awoken, see if there is an LCB, and if so start sending data. TUIdl: SINK← LCB, BUS=0; Is there an LCB? :OkLCB5; [OkLCB5, NoLCB5] ; Here when there is no LCB. NoLCB5: :Discard; Reset Send Enable, stay in Idle state ; There is an LCB. Begin transmission of a new frame. OkLCB5: L← TUBlk#, :ChangeStateGo; Enter new state ; 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, BUS=0, TASK; L← control command EIATMP← L, :TCCom; [TCCom, ~TCCom] Bypass if zero TCCom: T← 177000; Construct control register address T← LINE*4 OR T; = 177300 + 4*line MAR← 300 OR T; TASK; MD← EIATMP; Issue command ; Post completion and chain to next block, if any. ~TCCom: L← 0; L← post code 0 T← 3, :EIAPost; T← return index 3 EPRet3: L← TUIdl#, :ChangeStateGo; Enter idle state immediately ; *** Interval Timer Task *** ; Data structure (shared with CommProcTask): ; Words relative to LINTAB (recall that LINTAB is even) ; -4 interrupt bit mask, used when timer expires ; -3 time interval (units of 625 microseconds) ; -2 time counter (maintained by microcode) !1, 2, ITCont, ITDone; IntTimerTask: T← LINTAB; MAR← L← -2+T; Fetch current counter value MTEMP← L; MTEMP← LINTAB-2 L← MD-1, BUS=0; Decrement and test count :ITCont; [ITCont, ITDone] ; Timer has not expired, continue counting. ITCont: MAR← MTEMP; Store decremented count in memory TASK; ITLast: MD← M, :IntTimerTask; ; Here when timer expires. Issue interrupt and reset count. ITDone: MAR← -4+T; T← NWW; L← MD OR T; Merge word -4 (interrupt bit mask) T← MD-1; Time interval -1 MAR← MTEMP; Store into word -2 NWW← L, L← T, TASK, :ITLast; Cause interrupt, finish store of count