.MCALL .MODULE .MODULE EU,VERSION=5,COMMENT=,AUDIT=NO ; ; COPYRIGHT (c) 1995-1998 BY ; Alan R. Baldwin ; Kent State University ; Kent, Ohio 44242 ; ; ; ; This handler uses a mapped region for the packet ; buffers and may itself be mapped. ; ; devdef ,maph,noset do not specify 'dma' ; or 'handbuf' as the ; handler is designed ; to be mapped without ; special assistance ; from TSX+ ; .sbttl TSX-Plus TSGEN.MAC changes ; A change to the TSX-Plus TSGEN.MAC file is required ; to allocate 4 UNIBUS MAPPING REGISTERS for this driver ; when used with systems having more than 248. kbytes ; of addressable memory. ; ; The mapping registers are only required when more than ; 248. Kbytes of memory are used by TSX-Plus. ; ; The following was excerpted from TSGEN.MAC: ;************************************************************************** ; ; ; ; Generate tables for Unibus map registers ; ; ; .GLOBL UMRBAS,UMREND,UMRWHD ; UMRWHD: .WORD 0 ; .MACRO UMRDEF NUM ; .BYTE CURUMR ;UM$UMR ; .BYTE NUM ;UM$NMR ; .WORD NUM*4096. ;UM$WDS ; .WORD 0 ;UM$IOQ ; CURUMR = CURUMR+NUM ; .ENDM UMRDEF ; ; Define UMR sets in order of size -- small to large. ; CURUMR = 5 ;Map regs 0-4 always mapped by init code. ; UMRBAS: ; .IF EQ, ;Generate only for UNIBUS machines ; UMRDEF 1. ; UMRDEF 1. ; UMRDEF 4. ; UMRDEF 4. ; UMRDEF 8. ; UMRDEF 8. ; .ENDC ; UMREND: ;************************************************************************** ; ; Change the first 'UMRDEF 8.' to 'UMRDEF 4.' ; This will free the last 4 UNIBUS MAPPING REGISTERS. ; ; Mapping register #28 at 170354/170356 is used ; to map to the EU handler. ; ; Mapping register #29 at 170360/170362 is used ; to map to the xmit buffer. ; ; Mapping register #30 at 170364/170366 and ; Mapping register #31 at 170370/170372 are used for the ; EU handlers' data buffers. ; ; The mapping registers are only used when more than ; 248. Kbytes of memory are used by TSX-Plus. ; .sbttl Mapping Register Definitions ; The Mapping Register Addresses umr28l =: 170354 ; UMR #28 umr28h =: 170356 umr29l =: 170360 ; UMR #29 umr29h =: 170362 umr30l =: 170364 ; UMR #30 umr30h =: 170366 umr31l =: 170370 ; UMR #31 umr31h =: 170372 ; The Corresponding Unibus Addresses umr28b =: 060000 ; UMR #28 umr28x =: 3 umr29b =: 100000 ; UMR #29 umr29x =: 3 umr30b =: 120000 ; UMR #30 umr30x =: 3 umr31b =: 140000 ; UMR #31 umr31x =: 3 ei$una = 1 .include "ei.mac" .sbttl DEUNA Port Handler Definitions .audit .ei .audit .eu .mcall .assume .mcall .addr, .br .mcall .inten, .fork .if ne xm$22bt ; XM 22-BIT .error <; EU: XM 22-Bit Not Supported> .error <; EU: Not compatible with UBX> .endc .if ne xm$tsx ; xm/tsx .if ne .iif ndf rbcnt rbcnt = 10. ; XM/TSX High Memory .iff .iif ndf rbcnt rbcnt = 2. ; XM/TSX 256K Systems .endc .iff ; sb/fb .iif ndf rbcnt rbcnt = 2. ; SB/FB Low Memory .endc .iif lt rbcnt-2. .error <; at least 2 buffers required> .iif gt rbcnt-10. .error <; no more than 10 buffers allowed> .if ne xm$tsx ; xm/tsx .iif ndf bufsz bufsz = 1600. ; XM/TSX Packet Size .iff ; sb/fb .iif ndf bufsz bufsz = 600. ; SB/FB Low Memory .endc .iif lt bufsz-600. .error <; minumum buffer size is 600.> .iif gt bufsz-1600. .error <; maximum buffer size is 1600.> udbbsz =: <32.*2> .if ne xm$tsx ; xm/tsx tmpkt = ktgran*2 tmp1 = /tmpkt ememsz = tmp1+<</tmpkt>*rbcnt> .iff ; sb/fb ememsz = + udbbsz .endc c.jnum =: 6 c.comp =: 14 .sbttl DEUNA Port Handler Device Definitions un$cs0 =: 0 ; Port Control and Status Register 0 un$cs1 =: 2 ; Port Control and Status Register 1 un$cs2 =: 4 ; Port Control and Status Register 2 un$cs3 =: 6 ; Port Control and Status Register 3 c0.sei =: 100000 ; PCSR0 status error interrupt c0.cei =: 040000 ; PCSR0 port command error interrupt c0.rxi =: 020000 ; PCSR0 receive interrupt c0.txi =: 010000 ; PCSR0 transmit interrupt c0.dni =: 004000 ; PCSR0 done interrupt c0.bui =: 002000 ; PCSR0 receive buffer unavailable interrupt c0.fei =: 001000 ; PCSR0 fatal internal error interrupt c0.sci =: 000400 ; PCSR0 unsolicited state change interrupt c0.is =: 000200 ; PCSR0 interrupt summary c0.ie =: 000100 ; PCSR0 interrupt enable c0.rse =: 000040 ; PCSR0 reset c0.cmk =: 000017 ; PCSR0 port_command pc.nop =: 00 ; port_command no-op pc.gp =: 01 ; port_command get pcbb pc.gc =: 02 ; port_command get cmd pc.st =: 03 ; port_command selftest pc.sta =: 04 ; port_command start pc.boo =: 05 ; port_command boot pc.pd =: 10 ; port_command pdmd pc.hlt =: 16 ; port_command halt pc.sto =: 17 ; port_command stop c1.xok =: 100000 ; PCSR1 status error bit c1.cok =: 040000 ; PCSR1 code c1.emk =: 037400 ; PCSR1 self test and error codes c1.tmo =: 000200 ; PCSR1 port command timeout c1.did =: 000160 ; PCSR1 identification c1.smk =: 000017 ; PCSR1 state ps.res =: 00 ; state reset ps.pl =: 01 ; state primary load ps.rdy =: 02 ; state ready ps.run =: 03 ; state running ps.uh =: 05 ; state UNIBUS halted ps.nh =: 06 ; state NI halted ps.nuh =: 07 ; state NI and UNIBUS halted ps.ph =: 10 ; state port halted ps.sl =: 17 ; state secondary load .sbttl DEUNA Port Handler Data Structure Definitions pcb.f0 =: 0 ; port control block offsets pcb.f1 =: 1 pcb.f2 =: 2 pcb.f4 =: 4 pcb.f6 =: 6 pcb.sz =: 10 ; port control block size ; port control functions pf.nop =: 00 ; no-op pf.lsm =: 01 ; start microaddress pf.rda =: 02 ; read default physical address pf.rpa =: 04 ; read physical address pf.wpa =: 05 ; write physical address pf.rma =: 06 ; read multicast address list pf.wma =: 07 ; write multicast address list pf.rrf =: 10 ; read descriptor ring format pf.wrf =: 11 ; write descriptor ring format pf.rc =: 12 ; read counters pf.rcc =: 13 ; read and clear counters pf.rm =: 14 ; read mode register pf.wm =: 15 ; write mode register pf.rs =: 16 ; read status pf.rsc =: 17 ; read and clear status pf.dim =: 20 ; dump internal memory pf.lim =: 21 ; ;oad internal memory pf.rid =: 22 ; read system id pf.wid =: 23 ; write system id pf.rla =: 24 ; read load server address pf.wla =: 25 ; write load server address ; read/write mode bits, PCB(2) f2.pm =: 100000 ; promiscuous mode f2.am =: 040000 ; enable all f2.ddc =: 020000 ; disable data chaining f2.tpe =: 010000 ; transmit pad enable f2.ect =: 004000 ; enable collision test f2.dmm =: 001000 ; disable maintenance message f2.ilm =: 000100 ; internal loopback f2.dtc =: 000010 ; disable transmit crc f2.lop =: 000004 ; loop frames f2.hdm =: 000001 ; half duplex mode ; read/clear status bits, PCB(2) f2.es =: 100000 ; error summary f2.me =: 040000 ; multiple errors f2.bbl =: 020000 ; babl - transmit too long f2.cte =: 010000 ; collision test error f2.te =: 004000 ; UNIBUS timeout f2.rre =: 001000 ; receiver ring error f2.tre =: 000400 ; transmit ring error f2.pat =: 000200 ; rom patch f2.rmo =: 000100 ; ram microcode operational f2.rmk =: 000077 ; rom revision code ; transmit ring descriptor entries td.len =: 0 ; segment length in bytes td.adl =: 2 ; LSW address of segment td.adh =: 4 ; MSW address of segment plus bits td.sta =: 6 ; transmit status td.esz =: 10 ; length of transmit descriptor ; transmit specific descriptor bits ah.mat =: 020000 ; station match ah.mrt =: 010000 ; multiple retries needed ah.one =: 004000 ; one collision ; transmit status bits st.ble =: 100000 ; buffer length error st.uto =: 040000 ; UNIBUS timeout st.uf =: 020000 ; underflow error st.lco =: 010000 ; late collision st.lca =: 004000 ; loss of carrier st.rty =: 002000 ; retry st.tdr =: 001777 ; time domain reflectometry value ; receive ring descriptor entries rd.len =: 0 ; length of receive data buffer in bytes rd.adl =: 2 ; LSW address of segment rd.adh =: 4 ; MSW address of segment plus bits rd.sta =: 6 ; status bits rd.esz =: 10 ; size of receive descriptor ; receiver specific descriptor bits ah.fe =: 020000 ; frame error ah.ovf =: 010000 ; frame overflow ah.crc =: 004000 ; cyclical redundancy check ; receive status bits st.nch =: 020000 ; no data chaining st.oe =: 010000 ; overrun error st.lmk =: 007777 ; message length ; common transmit/receive descriptor bits ah.own =: 100000 ; port ownership ah.ers =: 040000 ; error summary ah.def =: 002000 ; deferred ah.stp =: 001000 ; start of frame ah.enp =: 000400 ; end of frame ah.hom =: 000003 ; MSW bits ; common transmit/receive status bits st.ble =: 100000 ; buffer length error st.uto =: 040000 ; UNIBUS timeout .sbttl DEUNA Port Handler Installation Code .enabl lsb .drins ei mov @#sysptr,r1 ; pro's and QBUS systems not allowed bit #,confg2(r1) bne o.err mov inscsr,r0 .assume un$cs0 eq 0 mov #c0.rse,@r0 ; reset interface ; The DELUA appears to spuriosly set the c0.sci bit ; during the self test portion of the installation. ; Thus a time out period is used to terminate the ; test for the 'Done Interrupt' bit and report ; the failure of the installation routine. ; The loop counter value is set for a PDP-11/44 ; which executes the loop in about 5 microseconds. 1$: clr r2 ; loop counter set for mov #180.,r1 ; 60 seconds on a PDP-11/44 2$: dec r2 ; loop will terminate on bne 3$ ; 'done' or 'time out' dec r1 beq o.err .assume un$cs0 eq 0 3$: bit #c0.dni,@r0 ; wait until done beq 2$ movb un$cs0+1(r0),un$cs0+1(r0) movb un$cs1(r0),-(sp) bic #^c,(sp) cmpb (sp)+,#ps.rdy ; must be in ready state bne o.err asl #100000 bcc o.aok .assume un$cs0 eq 0 movb #pc.st,@r0 ; selftest interface br 1$ .dsabl lsb .assume . le 400,<;install area overflow> .sbttl DEUNA Port Handler Set Code .drset csr, 160000, o.csr, oct .drset vector, 500, o.vec, oct o.csr: cmp r0,r3 blo o.err mov r0,inscsr ; load all csr locations mov r0,discsr mov r0,eicsr br o.aok o.vec: cmp r0,r3 bhis o.err mov r0,eistrt ; load all vector locations mov r0,eivec o.aok: tst (pc)+ o.err: sec return .sbttl init - initialization code .psect eidvr ; init: (registers saved/restored) ; called from EI.MAC ; ; ini$: (registers not saved/restored) ; called from the rcvr/xmtr time-out routine tmo$ init: call sav50 ; save all registers ini$: chkpnt ; check point call clr.ie ; clear interrupt enable 10$: jsr r5,20$ ; load function .word c0.rse ; reset jsr r5,20$ ; load function .word pc.sto ; stop ; (1) The XM 22-bit handler is loaded in Low Memory ! ; (2) The TSX 22-bit handler may be loaded anywhere ! .if ne .addr #eistrt,r1 ; driver location call cvtphy ; anywhere !!! mov r1,@#umr28l ; load mapping registers mov r0,@#umr28h .endc ; (1) The XM 22-bit buffers may be anywhere in High Memory ! ; (2) The TSX 22-bit buffers may be anywhere in High Memory ! .if ne mov eixadr,r1 ; EU data buffers clr r0 ; prepare address ashc #6,r0 ; for UNIBUS MAPPING REGISTER mov r1,@#umr30l ; load mapping registers mov r0,@#umr30h add #20000,r1 adc r0 mov r1,@#umr31l ; load mapping registers mov r0,@#umr31h .endc .addr #pcb,r1 ; prepare port control block ; (1) The control block buffers are in the handler ! .if ne xm$tsx ; xm/tsx call cvtpgm ; convert to UNIBUS address mov r1,un$cs2(r2) ; control block address (LSW) bic #^c,r0 ; select MSW bits mov r0,un$cs3(r2) ; control block address (MSW) .iff ; sb/fb mov r1,un$cs2(r2) ; control block address (LSW) clr un$cs3(r2) ; control block address (MSW) .endc jsr r5,20$ ; load function .word pc.gp ; get PCBB address mov #pf.rda,pcb+pcb.f0 ; read default physical address jsr r5,20$ ; load function .word pc.gc ; get CMD mov #pf.wpa,pcb+pcb.f0 ; write physical address jsr r5,20$ ; load function .word pc.gc ; get CMD .addr #pcb+pcb.f2,r0 ; prepare to copy physical address .addr #eiphad,r1 mov #6.,r3 ; 6 bytes of address 15$: movb (r0)+,(r1)+ sob. r3,15$ mov #pf.rs,pcb+pcb.f0 ; read status jsr r5,20$ ; load function .word pc.gc ; get CMD clr maxmlt movb pcb+pcb.f4,maxmlt ; save maximum number ; of broadcast addresses return 20$: mov eicsr,r2 movb un$cs0+1(r2),un$cs0+1(r2) .assume un$cs0 eq 0 mov (r5)+,@r2 ; load function .assume un$cs0 eq 0 .assume c0.is eq 200 25$: tstb @r2 ; wait bpl 25$ movb un$cs0+1(r2),un$cs0+1(r2) rts r5 .sbttl resrng - Reset Ring Structures .psect eidvr ; resrng: (registers saved/restored) ; called from EI.MAC ; ; res$: (registers not saved/restored) ; called from the enable routine ena$ (enable) ; ; The DEUNA/DELUA Interrupts should be ; disabled when calling these routines. resrng: call sav50 ; save r0-r5 .if ne xm$tsx ; xm/tsx call savpar ; save kisarx .endc res$: chkpnt ; check point clr rcvidx ; reset receive index .addr #rbufad,r2 ; buffer address table .addr #rcvbdl,r3 ; receive 'Buffer Descriptor List' .addr #fqelem,r4 ; internal scratch queue element mov #rbcnt,r5 ; number of buffers .if ne xm$tsx ; xm/tsx mov #visarx,q$buff(r4) ; map to the beginning of the buffers mov eixadr,q$par(r4) 5$: mov q$buff(r4),(r2) ; load buffer address into table mov (r2)+,r1 ; load mapping mov q$par(r4),(r2) mov (r2)+,@#kisarx call cvtbuf ; convert to UNIBUS address .iff ; sb/fb mov eixadr,q$buff(r4) 5$: mov q$buff(r4),(r2) ; load buffer address into table mov (r2)+,r1 clr (r2)+ clr r0 .endc .assume rd.len eq 0 mov #bufsz,(r3) ; initialize the length mov r1,rd.adl(r3) ; load address bic #^c,r0 bis #ah.own,r0 ; and owner flag bit mov r0,rd.adh(r3) clr rd.sta(r3) ; initial values add #rd.esz,r3 ; next buffer location add #bufsz,q$buff(r4) .if ne xm$tsx ; xm/tsx call fixpar ; fixup new buffer address and par .endc sob. r5,5$ ; do all read buffers clr td.adl(r3) ; xmit bdl disabled clr td.adh(r3) .if ne xm$tsx ; xm/tsx mov q$buff(r4),(r2) ; load buffer address into table mov (r2)+,r1 ; load mapping mov q$par(r4),(r2) mov (r2)+,@#kisarx call cvtbuf ; convert to UNIBUS address mov r1,(r2)+ ; udbadr bic #^c,r0 mov r0,(r2)+ .iff ; sb/fb mov q$buff(r4),(r2) ; load buffer address into table mov (r2)+,r1 ; load mapping clr (r2)+ mov r1,(r2)+ ; udbadr clr (r2)+ .endc .if ne xm$tsx ; xm/tsx mov udbbad+2,@#kisarx ; setup descriptor ring format mov udbbad,r2 .addr #xmtbdl,r1 ; load xmit parameters call cvtpgm ; convert to UNIBUS address mov r1,(r2)+ bic #^c,r0 ; select MSW bits bis #*400,r0 mov r0,(r2)+ mov #1,(r2)+ ; single xmit descriptor .iff ; sb/fb mov udbbad,r2 .addr #xmtbdl,r1 ; load xmit parameters mov r1,(r2)+ mov #*400,(r2)+ mov #1,(r2)+ ; single xmit descriptor .endc .addr #rcvbdl,r1 ; load rcvr parameters .if ne xm$tsx ; xm/tsx call cvtpgm ; convert to UNIBUS address mov r1,(r2)+ bic #^c,r0 ; select MSW bits bis #*400,r0 mov r0,(r2)+ mov #rbcnt,(r2)+ .iff ; sb/fb mov r1,(r2)+ mov #*400,(r2)+ mov #rbcnt,(r2)+ .endc mov udbadr,pcb+pcb.f2 ; load port command buffer mov udbadr+2,pcb+pcb.f4 mov #pf.wrf,pcb+pcb.f0 jsr r5,ancfun ; load function .word pc.gc ; do it return .sbttl getadr - Get Buffer Descriptor Address .psect eidvr getadr: mov rcvidx,r1 ; low order result .if ne ei$eis mul #rd.esz,r1 ; is all that is needed .iff .assume rd.esz eq 10 asl r1 asl r1 asl r1 .endc .addr #rcvbdl,r1,add return .sbttl cvtphy - Convert to Physical Address .psect eidvr .if ne xm$tsx ; xm/tsx .if ne ; xm(18)/tsx(18) cvtpgm: cvtbuf: cvtxmt: .endc cvtphy: mov r1,-(sp) ; using the address bic #160000,(sp) ; in r1, find the physical ash #-12.,r1 ; address by using the bic #^c<16>,r1 ; corresponding mapping register mov kisar0(r1),r1 ; and the r1 offset clr r0 ashc #6,r0 add (sp)+,r1 adc r0 return .sbttl cvtxxx - Convert to UNIBUS Address .psect eidvr .if ne ; xm(22)/tsx(22) cvtpgm: call cvtphy ; first get physical address sub @#umr28l,r1 ; .assume physical > umr28l/h mov #umr28x,r0 ; make the new address point at the add #umr28b,r1 ; EU driver UNIBUS MAPPING REGISTER ;;; adc r0 ; EU limited to 8K length return cvtbuf: call cvtphy ; first get physical address mov eixadr,r0 ; get base address ash #6,r0 ; for UNIBUS MAPPING REGISTER sub r0,r1 ; calculate the offset and mov #umr30x,r0 ; make the new address point at the add #umr30b,r1 ; UNIBUS MAPPING REGION ;;; adc r0 ; buffer limited to 16K length return cvtxmt: mov r1,-(sp) mov @#kisarx,r1 ; EU xmit buffer location clr r0 ; prepare address ashc #6,r0 ; for UNIBUS MAPPING REGISTER mov r1,@#umr29l ; load mapping registers mov r0,@#umr29h mov (sp)+,r1 call cvtphy ; first get physical address sub @#umr29l,r1 ; calculate the offset and mov #umr29x,r0 ; make the new address point at the add #umr29b,r1 ; xmit UNIBUS MAPPING REGION ;;; adc r0 ; xmit buffer limited to 8K length return .endc .endc .sbttl enable - Enables Interrupts .psect eidvr ; enable: (registers saved/restored) ; called from EI.MAC ; ; ena$: (registers not saved/restored) ; called from the rcvr/xmtr time-out routine tmo$ enable: call sav50 ; save r0-r5 call ena$ callr set.ie ; set interrupt enable ena$: .if ne xm$tsx ; xm/tsx call savpar ; save par .endc chkpnt ; check point mov #-1,xmitfg ; initialize the mov #-1,recvfg ; transmit and receive flags .if ne ; device timeout call rcvmr ; restart timer .endc call res$ ; setup the receive ring call set$ ; and setup the address parameters jsr r5,ancfun ; load function .word pc.pd ; check ring structures jsr r5,ancfun ; load function .word pc.sta ; start DEUNA return .sbttl disabl - Disable Interrupts .psect eidvr ; disabl: (registers saved/restored) ; called from EI.MAC ; ; dis$: (registers not saved/restored) ; called from the recr/xmtr time-out routine tmo$ ; disabl: call sav50 ; save r0-r5 dis$: call clr.ie ; clear interrupt enable chkpnt ; check point jsr r5,ancfun ; load function .word pc.sto ; stop the DEUNA .if ne ; device timeout call cxmtmr ; abort xmt timeout callr crcvmr ; abort rcv timeout .iff return .endc .sbttl setup - Update Address Filtering .psect eidvr ; setup: (registers saved/restored) ; called from EI.MAC ; ; set$: (registers not saved/restored) ; called from the enable routine ena$ (enable) ; ; The DEUNA/DELUA Interrupts should be ; disabled when calling set$. setup: call sav50 .if ne xm$tsx ; xm/tsx call savpar .endc .if ne ei$psw bis #340,@#psw ; hold interrupts .iff mtps #340 .endc mov @eicsr,-(sp) ; save status call clr.ie ; clear interrupt enable .if ne ei$psw bic #340,@#psw ; lower priority .iff mtps #0 .endc call set$ bit #,(sp)+ ; interrupts enabled ? beq 5$ ; no - skip set.ia = . call set.ie ; set interrupt enable .if ne ei$psw bic #340,@#psw ; and allow interrupts .iff mtps #0 .endc 5$: return set$: chkpnt ; check point .if ne xm$tsx mov udbbad+2,@#kisarx ; access the UDB buffer .endc mov udbbad,r1 .addr #eibrod,r0 ; list of broadcast addresses mov #9.,r2 ; eibrod + 8 units clr r3 ; number of loaded addresses 5$: mov (r0)+,-(sp) ; is this an address ? bis (r0)+,(sp) bis (r0)+,(sp)+ beq 10$ ; no - skip sub #ua.esz,r0 ; yes - copy to buffer mov (r0)+,(r1)+ mov (r0)+,(r1)+ mov (r0)+,(r1)+ inc r3 ; one more broadcast address cmpb r3,maxmlt ; compare to maximum bgt 20$ ; too many - exit 10$: sob. r2,5$ ; test complete list mov udbadr,pcb+pcb.f2 ; load UDB buffer address into PCB movb udbadr+2,r5 swab r3 ; combine MSW bits and number of bis r3,r5 ; broadcast addresses to load mov r5,pcb+pcb.f4 mov #pf.wma,pcb+pcb.f0 ; write broadcast addresses jsr r5,ancfun ; load function .word pc.gc clr r5 tst eipmfg ; promiscuous ? beq 15$ ; no - skip bis #f2.pm,r5 15$: bis #,r5 ; disable maintenance and mov r5,pcb+pcb.f2 ; set half duplex mode mov #pf.wm,pcb+pcb.f0 ; write mode jsr r5,ancfun ; load function .word pc.gc 20$: return .sbttl Clear/Set Interrupt Enable .psect eidvr ; The DEUNA should be issued noop commands only ; when the interrupt enable bit is being changed. clr.ie: .if ne ei$psw movb @#psw,-(sp) ; save status bis #340,@#psw ; hold off interrupts .iff mfps -(sp) mtps #340 .endc .assume un$cs0 eq 0 bit #,@eicsr beq 5$ ; is cleared .assume un$cs0 eq 0 movb #,@eicsr ; clear interrupt enable bit 5$: .if ne ei$psw movb (sp)+,@#psw ; restore status .iff mtps (sp)+ .endc return set.ie: .if ne ei$psw bis #340,@#psw ; hold off interrupts .iff mtps #340 .endc .assume un$cs0 eq 0 bit #,@eicsr bne 5$ ; is set .assume un$cs0 eq 0 movb #,@eicsr ; set interrupt enable bit 5$: return ; donot allow interrupts .sbttl ancfun - Execute Ancillary Function .psect eidvr ; ancfun: ancilliary function execution ; called by various service routines to ; process a port command. ; ; Interrupts must not be enabled when ; calling this function. (A rcvr/xmtr time-out always ; enables interrupts, thus the inclusion of a call ; to clr.ie) ancfun: call clr.ie ; clear interrupt enable ; just in case ! clr r0 ; wait time mov eicsr,r2 movb #,1(r2) ; clear DNI bit .assume un$cs0 eq 0 mov (r5),@r2 ; load function .assume un$cs0 eq 0 5$: bit #,@r2 ; wait until complete bne 10$ sob. r0,5$ ; time-out ? br ancfun ; yes - retry 10$: movb #,1(r2) ; clear DNI bit tst (r5)+ ; skip argument rts r5 .sbttl eiint - Interrupt Entry Point .psect eidvr euabrt: call clr.ie ; stop interrupts .if ne call cxmtmr ; abort xmt timeout call crcvmr ; abort rcv timeout .endc call eiabrt ; go to the abort code tst portct ; anything enabled ? bpl 5$ ; yes - do enables return 5$: .if ne call rcvmr ; yes - restart rcv timeout tst eiocqe ; something to send ? beq 10$ ; no - skip xmt timeout startup call xmtmr ; restart xmt timeout 10$: .endc callr set.ia ; allow interrupts .drast ei,ei$pri,euabrt mov eicsr,r5 movb #,@r5 ; disable interrupts .if ne ei$dbg inc (pc)+ ; reentry flag irqflg: .word -1 beq 5$ debug 3 dec irqflg ; fix entry error return 5$: .endc .if ne ei$frk .fork eufblk ; go to fork level .endc .if ne ei$chk call chkini ; initialize checkpointing .word en$1$2-. .endc chkpnt ; check point .assume un$cs0 eq 0 10$: movb un$cs0+1(r5),r4 ; get DEUNA status movb r4,un$cs0+1(r5) bit #/400,r4 ; skip if inactive beq 20$ bit #/400,r4 ; receive ? beq 15$ .if ne sp$210 add #1,eiipkt+2 ; count interrupt adc eiipkt .endc call euiint 15$: bit #/400,r4 ; transmit ? beq 10$ .if ne sp$210 add #1,eiopkt+2 ; count interrupt adc eiopkt .endc call euoint br 10$ ; loop 20$: bit #/400,r4 ; run out of buffers ? beq 25$ ; no - skip .if eq ei$frk call sav50 .endc jsr r5,ancfun ; load function .word pc.pd ; read descriptor rings 25$: chkpnt ; check point .if eq ei$dbg callr set.ie ; set interrupt enable .iff call set.ie ; but hold off interrupts dec irqflg ; reentry counter bmi 30$ debug 4 mov #-1,irqflg ; fix exit error 30$: return .endc .sbttl euiint - Routine to Process Receive Interrupts .psect eidvr .enabl lsb recv: call sav50 ; save r0-r5 call clr.ie ; clear interrupt enable call 5$ callr set.ia ; allow interrupts euiint: call sav50 ; save r0-r5 .if ne ; device timeout call crcvmr ; clear timer call 5$ callr rcvmr ; restart timer .endc 5$: chkpnt ; check point inc recvfg ; if we're busy, then just leave bne 20$ .if ne xm$tsx ; xm/tsx call savpar ; save the mapping .endc 10$: call getadr ; get the receive BDL address bit #ah.own,rd.adh(r1) ; buffer ready beq 30$ ; then check for errors 15$: dec recvfg ; if there is another frame bpl 10$ ; to process then loop 20$: return 25$: .if ne sp$210 tst eiquef ; donot throw away frames bne 15$ ; if frame queueing is enabled .endc br 70$ 30$: bit #ah.ers,rd.adh(r1) bne 70$ ; if there is an error ; then discard the frame 35$: mov eiicqe,r4 ; if there are no requests beq 25$ ; for frames then go check ; if frame queueing is enabled 40$: tst eipmfg ; if permiscuous mode is bne 85$ ; enabled, then just return the frame ; in the first queued request mov rcvidx,r2 asl r2 asl r2 .addr #rbufad,r2,add ; map the frame buffer mov (r2)+,r0 .if ne xm$tsx mov (r2),@#kisarx .endc .addr #eiupt,r3 ; check for a valid mov r3,-(sp) ; protocol mov #8.,r2 .assume uo.esz eq up.esz 45$: tst eiuot-eiupt(r3) beq 50$ cmp ef.typ(r0),(r3) beq 55$ 50$: tst (r3)+ sob. r2,45$ tst (sp)+ br 70$ ; discard frame if no match 55$: mov r3,r2 sub (sp)+,r3 .assume uo.esz eq up.esz add #eiuot-eiupt,r2 .assume uo.job eq 0 .assume uo.ofg eq 1 tst (r2) ; is the unit open ? beq 70$ ; no (?!) - requeue frame .addr #eiphad,r4 ; check for physical address cmp 4(r0),4(r4) bne 60$ cmp 2(r0),2(r4) bne 60$ cmp (r0),(r4) beq 75$ 60$: bit #1,ef.dst(r0) ; multicast address ? beq 70$ ; no - requeue frame add #6,r4 ; or broadcast address cmp 4(r0),4(r4) bne 65$ cmp 2(r0),2(r4) bne 65$ cmp (r0),(r4) beq 75$ 65$: .if ne ei$eis .assume up.esz eq 2 .assume ua.esz eq 6 mul #,r3 .iff .assume up.esz eq 2 .assume ua.esz eq 6 mov r3,-(sp) ; * 1 asl r3 ; * 2 add (sp)+,r3 ; (* 2) + (* 1) .endc .addr #eiuat,r3,add ; or multicast address cmp 4(r0),4(r3) bne 70$ cmp 2(r0),2(r3) bne 70$ cmp (r0),(r3) beq 75$ 70$: br 100$ ; if none match then discard frame 75$: .addr #eiicqe-q$link,r4 ; now search the queued requests ; for a matching type 80$: mov q$link(r4),r4 ; if none match, then beq 25$ ; check if frame queueing is cmpb (r2),q$unit(r4) ; enabled before discarding bne 80$ ; the frame 85$: mov rd.sta(r1),r5 ; get the byte count in the bic #^c,r5 ; frame sub #4,r5 .if ne xm$tsx ; xm/tsx mov r5,-(sp) add #2,q$buff(r4) call @$ptwrd ; place byte count in second sub #4,q$buff(r4) ; word of user buffer .iff ; sb/fb mov q$buff(r4),r2 ; place byte count in second mov r5,2(r2) ; word of user buffer .endc inc r5 asr r5 mov q$wcnt(r4),r2 ; transfer upto but not sub #2,r2 ; exceeding the number of cmp r5,r2 ; words specified in the ble 90$ ; input request .if ne xm$tsx ; xm/tsx mov #rc.tru,-(sp) ; report an error if frame call @$ptwrd ; was longer than the input sub #2,q$buff(r4) ; buffer .iff ; sb/fb mov #rc.tru,@q$buff(r4) ; report an error .endc bis #hderr$,@q$csw(r4) ; set error status mov r2,r5 90$: mov rcvidx,r2 ; copy the data from the asl r2 ; handlers buffer into asl r2 ; the users' buffer .addr #rbufad,r2,add .if ne xm$tsx ; xm/tsx mov 2(r2),r1 ; source address mov (r2),r2 mov r4,-(sp) mov q$par(r4),r3 ; destination address mov q$buff(r4),r4 add #4,r4 mov @#sysptr,r0 ; r5 is the word count mov p1ext(r0),r0 call blkmov(r0) mov (sp)+,r4 .iff ; sb/fb mov (r2),r2 mov q$buff(r4),r3 add #4,r3 95$: mov (r2)+,(r3)+ ;move data sob. r5,95$ .endc .if ne sp$210 movb q$unit(r4),r0 ; update stats bic #^c,r0 asl r0 asl r0 .addr #eirun0,r0,add add #1,2(r0) adc (r0) .endc call eiideq ; now dequeue the request 100$: call getadr ; rebuild the receive BDL clr rd.sta(r1) movb #,rd.adh+1(r1) 105$: mov rcvidx,r1 inc r1 cmp r1,#rbcnt blt 110$ clr r1 110$: mov r1,rcvidx jmp 10$ .dsabl lsb .sbttl euoint - Routine to Process Transmit Interrupts .psect eidvr .enabl lsb euoint: call sav50 ; save r0-r5 .if ne ; device timeout call cxmtmr ; clear timer .endc chkpnt ; check point mov #-1,xmitfg ; reset the xmit flag .addr #xmtbdl,r5 ; address of the xmit descriptor mov eiocqe,r4 beq 20$ bit #ah.ers,td.adh(r5) ; error ? beq 15$ ; no - skip bit #st.lca,td.sta(r5) ; loss of carrier ? beq 5$ mov #,-(sp) br 10$ 5$: bit #st.rty,td.sta(r5) ; too many retries ? beq 15$ mov #,-(sp) 10$: bis #hderr$,@q$csw(r4) .if ne xm$tsx ; xm/tsx call @$ptwrd .iff ; sb/fb mov (sp)+,@q$buff(r4) .endc 15$: .if ne sp$210 movb q$unit(r4),r0 ; update stats bic #^c,r0 asl r0 asl r0 .addr #eixun0,r0,add add #1,2(r0) adc (r0) .endc call eiodeq br 25$ 20$: return xmit: call sav50 ; save r0-r5 call clr.ie ; clear interrupt enable call 25$ callr set.ia ; allow interrupts 25$: chkpnt ; check point mov eiocqe,r4 ; if there is nothing more to beq 20$ ; do or we are already busy inc xmitfg ; then exit bne 20$ .if ne xm$tsx ; xm/tsx call savpar ; save the mapping mov q$par(r4),@#kisarx .endc mov q$buff(r4),r2 add #<4+ef.src>,r2 ; if the source address is mov (r2),-(sp) ; not specified then load bis 2(r2),(sp) ; the station physical address bis 4(r2),(sp)+ bne 30$ .addr #eiphad,r1 mov (r1)+,(r2)+ mov (r1)+,(r2)+ mov (r1)+,(r2)+ 30$: mov q$buff(r4),r1 ; load buffer address add #4,r1 ; correct .if ne xm$tsx ; xm/tsx call cvtxmt ; convert to UNIBUS address .iff ; sb/fb clr r0 .endc .addr #xmtbdl,r3 ; transmit BDL mov q$wcnt(r4),r2 ; word count sub #2,r2 ; corrected asl r2 ; word -> byte count .assume td.len eq 0 mov r2,(r3)+ .assume td.adl eq 2 mov r1,(r3)+ bic #^c,r0 bis #,r0 .assume td.adh eq 4 mov r0,(r3)+ .assume td.sta eq 6 clr (r3) .if ne ; device timeout call xmtmr ; setup timeout .endc jsr r5,ancfun ; load function .word pc.pd ; check descriptors return .dsabl lsb .sbttl DEUNA Port Handler Watchdog routines .psect eidvr .if ne ; device timeout ; A Receiver timeout usually indicates a very inactive ; ethernet (no packets received in 4 minutes) or an ; internal failure by the DEUNA. An internal failure ; is assumed and the DEUNA is restarted. Any internally ; bufferred packets are lost during the initialization ; process. rcvmr: tst rcvblk+c.comp bne 10$ .if ne rt$sys mov eiicqe,r4 beq 10$ movb q$jnum(r4),r4 asr r4 asr r4 asr r4 bic #^C<16>,r4 mov r4,rcvblk+c.jnum .endc .addr #rcvtmo,r4 mov r4,rcvblk+c.comp .timio rcvblk,0,240.*60. ; 4 minutes 10$: return crcvmr: tst rcvblk+c.comp beq 10$ .ctimio rcvblk bcs 10$ clr rcvblk+c.comp 10$: return ; A transmitter timeout usually indicates an internal ; failure by the DEUNA. There is no recovery method ; other than restarting the DEUNA. Any internally ; bufferred packets are lost during the initialization ; process. xmtmr: tst xmtblk+c.comp bne 10$ .if ne rt$sys mov eiocqe,r4 beq 10$ movb q$jnum(r4),r4 asr r4 asr r4 asr r4 bic #^C<16>,r4 mov r4,rcvblk+c.jnum .endc .addr #xmttmo,r4 mov r4,xmtblk+c.comp .timio xmtblk,0,5.*60. ; 5 seconds 10$: return cxmtmr: tst xmtblk+c.comp beq 10$ .ctimio xmtblk bcs 10$ clr xmtblk+c.comp 10$: return ; receiver and xmtr timeout processing rcvtmo: clr rcvblk+c.comp ; V5.3 SJ may not ; clear c.comp (#!%$@) .if ne sp$210 add #1,eirtmo+2 ; count timeouts adc eirtmo .endc br tmo$ xmttmo: clr xmtblk+c.comp ; V5.3 SJ may not ; clear c.comp (#!%$@) .if ne sp$210 add #1,eixtmo+2 ; count timeouts adc eixtmo .endc tmo$: .if ne rt$sys call clr.ie ; disable DEUNA mov (sp),-(sp) ; fake an interrupt clr 2(sp) .if ne ei$psw bis #340,@#psw .iff mtps #340 .endc .inten 0,pic .fork tmfblk call cxmtmr ; abort xmt timeout call crcvmr ; abort rcv timeout .if ne ei$chk call chkini ; initialize checkpointing .word et$1$2-. .endc .iff call sav50 .if ne ei$chk call chkini ; initialize checkpointing .word et$1$2-. .endc call dis$ ; disable DEUNA .endc mov eiocqe,r4 ; xmit queued ? beq 5$ ; no - finished mov q$link(r4),eiocqe ; remove xmit queue element clr q$link(r4) ; and report error .if ne xm$tsx ; xm/tsx mov #,-(sp) bis #hderr$,@q$csw(r4) call @$ptwrd .iff ; sb/fb mov #,@q$buff(r4) bis #hderr$,@q$csw(r4) .endc call eixdeq 5$: call ini$ ; initialize call ena$ ; enable DEUNA callr set.ia ; set interrupt enable .endc .SBTTL Port Handler Inpure Data Area .psect eidat einame: .word ei$hnd .word ei$znd pcb: .word pf.rda .word 0,0,0 rbufad: .blkw udbbad: .blkw 2 udbadr: .blkw 2 rcvbdl: .blkb xmtbdl: .blkb td.esz rcvidx: .blkw recvfg: .word -1 xmitfg: .word -1 fqelem: .blkw 8. maxmlt: .word 0 .if ne ei$frk + eufblk: .word 0,0,0,0 .endc .if ne rt$sys * tmfblk: .word 0,0,0,0 .endc .if ne ; device timeout rcvblk: .word 0,0,0 .word 0 .word 177000+ei$cod .word -1 .word 0 xmtblk: .word 0,0,0 .word 0 .word 177100+ei$cod .word -1 .word 0 .endc eixbfr: .if ne rt$sbfb .blkw /2 .endc eislen =: <.-eisblk>/2 .sbttl Load/Fetch Once Only Code .psect eiend .enabl lsb .if eq ei$msw once:: .if ne xm$tsx ; xm/tsx mov @r5,r3 ; base address of handler sub #eibase,r3 mov @#sysptr,r4 mov p1ext(r4),r4 mov #einame,r5 add r3,r5 call findgr(r4) ; try to find the region bcc 20$ ; if the region has not mov @#sysptr,r4 ; been allocated then mov memptr(r4),r0 ; proceed to set up add r4,r0 ; the local region mov corptx(r0),r5 add r4,r5 5$: cmp #-1,(r5)+ bne 5$ 10$: cmp #-1,@r5 beq on.bad tst @r5 beq 15$ add #gr.esz,r5 br 10$ 15$: mov p1ext(r4),r0 mov #ememsz,r2 mov r3,-(sp) call xalloc(r0) mov (sp)+,r3 bcs on.bad mov r2,(r5)+ mov r1,(r5)+ mov #gr.pvt,(r5)+ mov #ei$hnd,(r5)+ mov #ei$znd,@r5 20$: mov r1,eixadr(r3) .iff ; sb/fb mov @r5,r3 ; base address of handler sub #eibase,r3 mov r3,eixadr(r3) add #eixbfr,eixadr(r3) .endc on.goo: tst (pc)+ on.bad: sec return .endc .dsabl lsb .end