.MCALL .MODULE .MODULE NU,VERSION=16,COMMENT=,AUDIT=NO ; Copyright (c) 1998 by Mentec, Inc., Nashua, NH. ; All rights reserved ; ; This software is furnished under a license for use only on a ; single computer system and may be copied only with the ; inclusion of the above copyright notice. This software, or ; any other copies thereof, may not be provided or otherwise ; made available to any other person except for use on such ; system and to one who agrees to these license terms. Title ; to and ownership of the software shall at all times remain ; in Mentec, Inc. ; ; The information in this document is subject to change without ; notice and should not be construed as a commitment by Digital ; Equipment Corporation, or Mentec, Inc. ; ; Digital and Mentec assume no responsibility for the use or ; reliability of its software on equipment which is not supplied ; by Digital or Mentec, and listed in the Software Product ; Description. .SBTTL CONDITIONAL ASSEMBLY SUMMARY ;+ ;COND ; RBUFCT (6) number of receive buffers ; ; MMG$T std conditional must be 1 ; TIM$IT std conditional = 0, rejects boot-time ; installation. = 1, Generates code ; which uses a timer to determine if ; hardware self-test has failed. ; ERL$G std conditional (no code effects) ;- NI$UNA = 1 ;We're building NU .INCLUDE "SRC:NI.MAC" ;Include the class handler code .SBTTL DEUNA Port Handler Edit History ;+ ; ; Original version: ; ; V05 (000) 22-Dec-85 Start of coding for DEUNA port support. ; MBG Initial coding completed 03-Jan-86. Now ; testing prior to V5.4 field test. ; ; V05 (001) 05-Feb-86 IS bit is not set in CSR following a NOP port ; MBG command. DISABL was waiting for it, hanging ; system for varying lengths of time. Install ; code moved to overlay, uses memory allocation ; method from VM. Set code moved to overlay, SET ; NU SHOW uses clean method of obtaining station ; address. ; ; V05 (002) 21-Feb-86 Changes due to code review ; MBG ; ; V05 (003) 01-Apr-86 FIXES: Port state not checked correctly, should ; MBG be masked first; register not saved/restored in ; RESRNG; wrong bit checked at install-time to ; determine results of self-test; channel 0 needs ; to be purged prior to use in SET SHOW. ; ; NEW: Added definitions for DELUA support; SET SHOW ; identifies device type (DEUNA vs. DELUA). ; ; V05 (005) 06-Jul-87 Code to reject boot-time install to avoid boot ; MBG/MLY hanging due to DEUNA/DELUA not having loopback ; nor attached to Ethernet. Also, code to ensure ; thorough self-test,setting reset bit is not sufficient ; if board has passed self-test since last power-on, ; that's why explicit "self-test" command is issued. ; ; V05 (007) 04-Nov-88 V5.5: Added code to make use of timers for self-test ; MBG timeout. If device timeout support not selected, ; handler will reject boot-time install. ; ; V05 (009) 21-Nov-88 V5.5: Added temporary support for UB for field ; MBG test pass 1. NU rejects installation if UB is ; active. ; ; V05 (011) 05-Dec-88 V05.05: Added support for UMR (UB) ; MBG ; ; V05 (012) 28-Apr-89 Ensure elimination of global region prior to ; MBG creation ; ; (013) 26-Sep-90 MBG Changed names of LOAD and UNLOAD routines to ; more handler-specific names the common names ; can be used in NI. ; ; (014) 16-Nov-90 MBG Performance enhancement - Store location of ; BLKMOV routine for quick call rather than ; determining it each time needed. ; ; (015) 24-Aug-91 MBG Performance enhancement - multicast address ; scan need not be done if frame destination is ; not a multicast address. ; ; (016) 2-Jan-97 ARB Add special functions: ; spfun 206 - frame queueing ; spfun 207 - physical ethernet address ; spfun 210 - handler status block ; ;- .SBTTL DEUNA Port Handler Definitions ; Here we set the audit trail .AUDIT .NI ;Class handler .AUDIT .NU ;Port handler ; RT-11 Macros we're going to use .MCALL .ADDR, .ASSUM, .BR ;General use .MCALL .WAIT ;Root install code .MCALL .MRKT, .CMKT ;Overlay install code .MCALL .READC ;Overlay set code ; Extended memory region .IIF NDF RBUFCT RBUFCT = 6. ;Number of default receive buffers RBUFSZ =: 1600. ;Size of receive buffers XBUFCT =: 1 ;Need one buffer for transmit XBUFSZ =: 1600. ; of this size UDBBSZ =: <32.*2> ;Size of 'Unibus Data Block' EMEMSZ = <++UDBBSZ>/2 EMEMSZ = >/KTGRAN .Assume RBUFCT GE 1 MESSAGE= .Assume XBUFCT EQ 1 MESSAGE= ; Parameters for UMR support MXPUMR ==: 7 ;Maximum permanent UMR's allowed RUMRCT ==: <+XBUFSZ+UDBBSZ+17777>/2&77777/10000+1 ;Permanent UMR's required .Assume RUMRCT LE MXPUMR MESSAGE= ; Define some local macros .MACRO BEQ. DST,?LOC BNE LOC JMP DST LOC: .ENDM ;BEQ. .MACRO BNE. DST,?LOC BEQ LOC JMP DST LOC: .ENDM ;BNE. ; Miscellaneous definitions BLOCK0 =: 0 ;Block 0 bias BLOCK1 =: 1000 ;Block 1 bias .SBTTL DEUNA Port Handler Device Definitions ; Note: ; All definitions are common to DEUNA and DELUA unless ; otherwise noted. ; Device Control/Status register offsets (from device register base) UN$CS0 =: 0 ;Port control/status register 0 UN$CS1 =: 2 ;Port control/status register 1 UN$CS2 =: 4 ;Port control/status register 2 UN$CS3 =: 6 ;Port control/status register 3 ; PCSR0 Bit definitions C0.SEI =: 100000 ;Status error interrupt C0.CEI =: 040000 ;Command error interrupt C0.RXI =: 020000 ;Receive ring interrupt C0.TXI =: 010000 ;Transmit ring interrupt C0.DNI =: 004000 ;Done interrupt C0.BUI =: 002000 ;Receive buffer unavailable interrupt ; 001000 ;DEUNA: reserved C0.FEI =: 001000 ;DELUA: Fatal error interrupt C0.SCI =: 000400 ;Unsolicited state change interrupt C0.IS =: 000200 ;Interrupt summary ; (OR of bits <15:08>) C0.IE =: 000100 ;Interrupt enable C0.RSE =: 000040 ;DEUNA reset ; 000020 ;reserved C0.CMK =: 000017 ;Port command mask ; Port commands PC.NOP =: 00 ;No-op, no DNI PC.GP =: 01 ;Get PCB PC.GC =: 02 ;Get command PC.ST =: 03 ;Self-test PC.STA =: 04 ;Start PC.BOO =: 05 ;Boot ; 06-07 ;reserved ; DEUNA: handled as no-op, no DNI ; DELUA: handled as no-op, sets DNI PC.PD =: 10 ;Polling demand ; 11-15 ;reserved, handled as no-op, sets DNI ; 16 ;DEUNA: reserved, handled as no-op, ; sets DNI PC.HLT =: 16 ;DELUA: Halt PC.STO =: 17 ;Stop ; PCSR1 Bit definitions C1.XOK =: 100000 ;DEUNA: Transceiver power ok C1.COK =: 040000 ;DEUNA: Port/Link cabling ok C1.EMK =: 037400 ;Self-test error code mask ;DELUA: C1.EMK =: 177400 C1.TMO =: 000200 ;Port command time-out ; 000160 ;DEUNA: reserved, <06:04> = 0 C1.DID =: 000160 ;DELUA: DELUA ID, <06:04> = 1 C1.SMK =: 000017 ;Port state mask ; Port states PS.RES =: 00 ;Reset PS.PL =: 01 ;Primary load PS.RDY =: 02 ;Ready PS.RUN =: 03 ;Running ; 04 ;reserved PS.UH =: 05 ;Unibus halted PS.NH =: 06 ;NI halted PS.NUH =: 07 ;NI and Unibus halted ; 10 ;DEUNA: reserved PS.PH =: 10 ;DELUA: Port halted ; 11-16 ;reserved ; 17 ;DEUNA: reserved PS.SL =: 17 ;DELUA: Secondary load ; Miscellaneous definitions .SBTTL DEUNA Port Handler Data Structure Definitions ; Port Control Block (PCB) PCB.F0 =: 0 ;Port function PCB.F1 =: 1 ;Port function dependent PCB.F2 =: 2 ;Port function dependent PCB.F4 =: 4 ;Port function dependent PCB.F6 =: 6 ;Port function dependent PCB.SZ =: 10 ;PCB size ; Port function codes PF.NOP =: 00 ;No-op PF.LSM =: 01 ;Load and start microaddress PF.RDA =: 02 ;Read default physical address ; 03 ;no-op 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 ring format PF.WRF =: 11 ;Write ring format PF.RC =: 12 ;Read counters PF.RCC =: 13 ;Read and clear counters PF.RM =: 14 ;Read mode PF.WM =: 15 ;Write mode PF.RS =: 16 ;Read port status PF.RSC =: 17 ;Read and clear port status PF.DIM =: 20 ;Dump internal memory PF.LIM =: 21 ;Load internal memory PF.RID =: 22 ;Read system ID parameters PF.WID =: 23 ;Write system ID parameters PF.RLA =: 24 ;DELUA: Read load server address PF.WLA =: 25 ;DELUA: Write load server address ; Funtions 14/15 - Read/Write mode PCB bit definitions F2.PM =: 100000 ;Promiscuous mode F2.AM =: 040000 ;All Multicast F2.DDC =: 020000 ;Disable Data Chaining on receive F2.TPE =: 010000 ;Transmit message Pad Enable F2.ECT =: 004000 ;Enable Collision Test ; 002000 ;must be zero F2.DMM =: 001000 ;Disable Maintenance Message ; 000600 ;must be zero ; 000100 ;DEUNA: must be zero F2.ILM =: 000100 ;DELUA: Internal loopback mode ; (Industrial Light & Magic?) ; 000060 ;must be zero F2.DTC =: 000010 ;Disable Transmit CRC F2.LOP =: 000004 ;Loopback ; 000002 ;must be zero F2.HDM =: 000001 ;DEUNA: Half-Duplex Mode ;DELUA: ignored ; Functions 16/17 - Read/Read and clear Port Status PCB bit definitions F2.ES =: 100000 ;Error summary F2.ME =: 040000 ;Multiple errors ; 020000 ;DEUNA: reserved F2.BBL =: 020000 ;DELUA: Babble F2.CTE =: 010000 ;Collision Test Error F2.TE =: 004000 ;Time-out Error ; 002000 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 Mask ; Unibus data block (UDB) ; Size and contents are dependent upon the function being ; performed. ; Transmit buffer descriptor TD.LEN =: 0 ;Buffer length TD.ADL =: 2 ;Address descriptor bits (lo-order) TD.ADH =: 4 ;Address descriptor bits (hi-order) TD.STA =: 6 ;Status word TD.ESZ =: 10 ;Transmit descriptor size ; Address descriptor bit definitions AH.OWN =: 100000 ;Ownership flag AH.ERS =: 040000 ;Error summary (OR of conditions ; reported in status word) AH.MAT =: 020000 ;Station match AH.MRT =: 010000 ;Multiple retries needed AH.ONE =: 004000 ;One retry needed AH.DEF =: 002000 ;Deferred AH.STP =: 001000 ;Start of packet AH.ENP =: 000400 ;End of packet ; 000374 ;reserved AH.HOM =: 000003 ;Hi-order address mask ; Status word bit definitions ST.BLE =: 100000 ;Buffer length error ST.UTO =: 040000 ;Unibus time-out ; 020000 ;DEUNA: reserved ST.UF =: 020000 ;DELUA: Underflow ST.LCO =: 010000 ;Late collision ST.LCA =: 004000 ;Loss of carrier ST.RTY =: 002000 ;Retry ST.TDR =: 001777 ;Time domain reflectometry value mask ; Receive buffer descriptor RD.LEN =: 0 ;Buffer length RD.ADL =: 2 ;Address descriptor bits (lo-order) RD.ADH =: 4 ;Address descriptor bits (hi-order) RD.STA =: 6 ;Status word RD.ESZ =: 10 ;Receive descriptor size ; Address descriptor bit definitions ; (same as for transmit, with following exceptions) AH.FE =: 020000 ;Framing error AH.OVF =: 010000 ;Frame overflow AH.CRC =: 004000 ;CRC error ; Status word bit definitions ; (same as for transmit, with following exceptions) ST.NCH =: 020000 ;No data chaining ; 010000 ;DEUNA: reserved ST.OE =: 010000 ;DELUA: Overrun ST.LMK =: 007777 ;Message length mask .SBTTL DEUNA Port Handler Installation Code ;+ ; ; Installation code is overlayed. The following code is used to ; determine boot-time install vs. KMON install. If boot-time ; install, channel 0 is open to the system volume and R3 points ; to a word containing the block number of the start of the handler ; file. If KMON install, channel 17 is open to the handler file. ; ;- .ENABL LSB .DRINS NI BR 10$ ;Data device installation 5$: SEC ;System device installation RETURN 10$: MOV R0,-(SP) ;Save R0 .WAIT #0 ;Boot-time install? MOV (SP)+,R0 ;*C* Restore R0 .IF EQ TIM$IT BCC 5$ ;Yes, then reject installation .IFF ;EQ TIM$IT BCS 20$ ;Nope... CLRB SEMTCH ;Yes, use channel 0 MOV @R3,HNDBLK ;Save bias to base of handler file .ENDC ;EQ TIM$IT 20$: MOV #INSOVR/2,R3 ;R3 = Address of installation code JMP GETOVR ;Use common overlay handler .DSABL LSB .Assume . LE 400 MESSAGE= .SBTTL DEUNA Port Handler Set Options .DRSET CSR, 160000, O.CSR, OCT .DRSET VECTOR, 500, O.VEC, OCT .DRSET SHOW, O.SHOW/2, GETOVR .SBTTL DEUNA Port Handler Set Code ; SET NU CSR=octal_address O.CSR: CMP R0,R3 ;Valid CSR address? BLO O.ERR ;Nope, reject the selection MOV R0,INSCSR ;Set the install-time MOV R0,DISCSR ; and display-time CSR's MOV R0,NICSR ;Let the handler know about it BR O.NORM ; SET NU VECTOR=octal_address O.VEC: CMP R0,R3 ;Valid VECTOR address? BHIS O.ERR ;Nope, reject the selection MOV R0,NISTRT ;Set the install-time vector MOV R0,NIVEC ;Let the handler know about it BR O.NORM .SBTTL DEUNA Port Handler Set/Install Code Overlay Handler ;+ ; ; GETOVR ; The general purpose overlay handler for install and set code. ; Reads the block containing the desired routine and dispatches ; to the routine via the appropriate entry point. ; ; CALL: ; R3 = address/2 of routine to execute ; ; GETBK1 ; Used to reload the overlayed block1 code. ; ; ** Note ** ; The blocks read by these routines overlay the block1 handler ; code. ; ;- .ENABL LSB GETBK1: MOV #O.EXIT/2,R3 ;Fake GETOVR into executing O.EXIT SWAB R3 MOV #1,SEMTBK ; after reloading block1 BR 5$ GETOVR: CMPB -(R3),-(R3) ;Preadjust R3 for 'NO' adjust NOP ; : Filler .Assume . EQ GETOVR+4 MESSAGE= CMPB (R3)+,(R3)+ ;Adjust for 'NO' entry point SWAB R3 ;Determine block of overlay MOVB R3,SEMTBK ;Set the block number to read 5$: HNDBLK =: .+2 ADD #.-.,SEMTBK ;Add bias to base of handler file .ADDR #BLOCK1,R5,PUSH ;R5 -> Buffer for overlay MOV R5,SEMTBF ;Set the buffer address MOV (SP)+,R5 ;Restore R5 JSR R0,10$ ;Save R0, R0 -> EMT block SEMTCH: .BYTE 17 ; : Channel SEMTFN: .BYTE 10 ; : Function (read) SEMTBK: .BLKW ; : Block number SEMTBF: .BLKW ; : Buffer address .WORD 256. ; : Word count .WORD 0 ; : Wait-mode I/O 10$: .READC CODE=NOSET ;*** .READW *** MOV (SP)+,R0 ;*C* Restore R0 BCS O.ERR ;In case of error CLRB R3 ;Reset block, offset in high byte SWAB R3 ;R3 = Word offset to overlay code ASL R3 ;R3 = Byte offset to overlay code ADD SEMTBF,R3 ;R3 -> Overlay code ; *** kludge? *** CMP SEMTBK,#1 ;Did we just restore block 1? BNE 20$ ;Nope, a real overlay ADD #,R3 ;Routine to execute is in block 0 20$: ; *** end kludge? *** JMP @R3 ;Dispatch to routine .DSABL LSB ; Common exit for everyone (including overlays) O.NORM: TST (PC)+ ;Normal return, carry clear O.ERR: SEC ;Error return, carry set RETURN O.EXIT: ROR R2 ;Restore carry from R2 RETURN .Assume . LE 1000 MESSAGE= .SBTTL LOAD - DEUNA Port Handler LOAD Code .SBTTL UNLOAD - DEUNA Port Handler UNLOAD Code ;+ ; ; LOAD ; o Locates the handler extended region and saves the ; bias required to map it. ; o Builds the buffer mapping table, used to BLKMOV buffers ; to and from user space. ; o Allocates permanent UMR's required to map the handler ; extended region, rejecting load if not available ; o Builds the buffer address table, used for building the ; BDL ring structure ; o Allocates the permanent UMR required to map the handler ; data base, rejecting load if not available ; o Informs the DEUNA/DELUA as to the location of the Port ; Control Block (PCB), through which commands will be ; issued. ; o Requests the DEUNA/DELUA default address (from ROM), sets ; this as the physical address and stores the address in the ; class handler data area. ; o Obtains, for later checks, the maximum multicast address ; list size allowed by the controller. ; ; UNLOAD ; o Deallocates any allocated UMR's on failure to load or ; during UNLOAD ; ;- .PSECT SETOVR LOAD:: ; Locate the handler extended region and save its base address MOV @#SYSPTR,R4 ;R4 -> Resident monitor MOV P1EXT(R4),R4 ;R4 -> PAR1 extern routine entry MOV @R5,R5 ;R5 -> Handler entry point MOV R5,-(SP) ;Save R5 for awhile ADD #,R5 ;R5 -> Handler region name CALL FINDGR(R4) ;Ask RT where the extended region is MOV (SP)+,R5 ;Restore previously saved R5 MOV GR.ADR(R1),(R5) ;Save the region base address ; Build the buffer mapping table (PAR1 bias and offsets) MOV R5,R4 ;Copy the handler entry point ADD #,R4 ;R4 -> Fake Qelement (at Q$BLKN) MOV #20000,Q$BUFF(R4) ;Start with beginning of PAR1 MOV R5,R2 ;Copy the handler entry point MOV (R2),Q$PAR(R4) ; using base of extended region ADD #,R2 ;R2 -> Address/PAR1 table for buffers MOV #RBUFCT,R3 ;R3 = Count of receive buffers 10$: MOV Q$BUFF(R4),(R2)+ ;Save address MOV Q$PAR(R4),(R2)+ ; and PAR1 bias for a receive buffer ADD #RBUFSZ,Q$BUFF(R4) ;Update buffer address CALL (R5) ;Normalize the Qelement SOB R3,10$ ;Do all the receive buffers .Assume XBFMTB EQ RBFMTB+<*2> MOV Q$BUFF(R4),(R2)+ ;Save address MOV Q$PAR(R4),(R2)+ ; and PAR1 bias for transmit buffer ADD #XBUFSZ,Q$BUFF(R4) ;On to UDB CALL (R5) ;Normalize the Qelement .Assume UDBMTB EQ XBFMTB+<*2> MOV Q$BUFF(R4),(R2)+ ;Save address MOV Q$PAR(R4),@R2 ; and PAR1 bias for UDB ; Allocate the UMR's required to map the handler extended region MOV #,R0 ;R0 = Count of UMR's required to ; map handler extended region CLR R1 ;R1 = Region base address <15:00> BISB (R5),R1 ; ... SWAB R1 ; ... CLR R2 ;R2 = Region base address <21:16> BISB (R5),R2 ; ... ROR R2 ;Now shift ROR R1 ; everything right ROR R2 ; two places ROR R1 ; into position MOV @#SYSPTR,R3 ;R3 -> Resident monitor MOV $H2UB(R3),R3 ;R3 -> UB routine vector table MOV R5,R4 ;R4 -> Extended region name ADD #,R4 ; ... CALL UBALL ;Allocate the permanent UMRs BCS 60$ ;Couldn't get them... ; Build the buffer address table MOV R5,R0 ;R0 -> Receive buffer address table ADD #,R0 ; ... MOV R3,-(SP) ;Save R3 across following loop MOV #RBUFCT,R3 ;R3 = Count of receive buffers 20$: MOV R1,(R0)+ ;Set receive buffer MOV R2,(R0)+ ; UMR address <17:00> ADD #RBUFSZ,R1 ;Increase address by size of buffer ADC R2 ; in two words SOB R3,20$ ;Back for more... MOV (SP)+,R3 ;Restore R3 .Assume XBFADD EQ RBFATB+<*2> MOV R1,(R0)+ ;Set transmit buffer MOV R2,(R0)+ ; UMR address <17:00> ADD #XBUFSZ,R1 ;Account for size of transmit buffer ADC R2 ; in two words .Assume UDBADD EQ XBFADD+<*2> MOV R1,(R0)+ ;Set UDB MOV R2,@R0 ; UMR address <17:00> ; Allocate a permanent UMR for NU driver data base MOV #1,R0 ;R0 = Count of UMR's required for ; mapping handler data base MOV R5,R1 ;R1 = Data base address <15:00> ADD #,R1 ; ... CLR R2 ;R2 = Data base address <21:16> MOV R5,R4 ;R4 -> Handler data base name ADD #,R4 ; ... CALL UBALL ;Allocate single UMR used for ; handler data base BCS 40$ ;Couldn't allocate it... MOV R5,R0 ;R0 -> Address tables ADD #,R0 ; ... MOV R1,(R0)+ ;Save the lo- MOV R2,(R0)+ ; and hi-order address of the ; Port Control Block ADD #,R1 ;Determine address of RCVBDL ADC R2 ; ... .Assume RBDLAD EQ > MOV R1,(R0)+ ;Save the lo- MOV R2,(R0)+ ; and hi-order address of the ; receive buffer descriptor list ADD #,R1 ;Determine address of XMTBDL ADC R2 .Assume XBDLAD EQ > MOV R1,(R0)+ ;Save the lo- MOV R2,@R0 ; and hi-order address of the ; transmit buffer descriptor list ; Now perform self-test MOV (R5),R0 ;R0 -> DEUNA device register base MOV (R5),UN$CS2(R0) ;Set the PCB lo- MOV (R5),UN$CS3(R0) ; and hi-order address .Assume UN$CS0 EQ 0 MOVB #PC.GP,@R0 ;Command = Get PCB address CALL LWAIT ;Wait for command completion ;;;error check? MOV #PF.RDA,(R5) ;Function = Read Default Address .Assume UN$CS0 EQ 0 MOVB #PC.GC,@R0 ;Command = Get Command CALL LWAIT ;Wait for command completion ;;;error check? ; Now, since the working physical address in use by this DEUNA may not ; be the default physical address (some other operating system has ; changed it for some reason), we force the working physical address ; to be the same as the default physical address. MOV #PF.WPA,(R5) ;Function = Write Physical Address .Assume UN$CS0 EQ 0 MOVB #PC.GC,@R0 ;Command = Get Command CALL LWAIT ;Wait for command completion ;;;error check? MOV R5,R0 ;R0 -> Returned station address ADD #,R0 ; ... MOV R5,R1 ;R1 -> Class handler storage ADD #,R1 ; ... MOV #6.,R2 ;R2 = Count of bytes 30$: MOVB (R0)+,(R1)+ ;Store a byte SOB R2,30$ ;Continue until done MOV (R5),R0 ;R0 -> DEUNA device register base MOV #PF.RS,(R5) ;Function = Read Port Status .Assume UN$CS0 EQ 0 MOVB #PC.GC,@R0 ;Command = Get Command CALL LWAIT ;Wait for command completion ;;;error check? CLR (R5) ;Reset maximum multicast list size MOVB (R5),(R5) ;Save controller limit BR 50$ 40$: CALL UNLOAD ;Failure, dump the UMR's we got BR 60$ ;And reject LOAD 50$: TST (PC)+ ;Good return (carry = 0) 60$: SEC ;Error return (carry = 1) RETURN ; UBALL ; Common code for allocating UMRs. UBALL: MOV R3,-(SP) ;Save some registers across CALL MOV R5,-(SP) CALL UB.ALL(R3) ;Allocate the single UMR for the ; handler data base MOV (SP)+,R5 ;*C* Restore previously MOV (SP)+,R3 ;*C* saved registers and RETURN ;*C* to caller UNLOAD:: MOV @#SYSPTR,R3 ;R3 -> Resident monitor MOV $H2UB(R3),R3 ;R3 -> UB routine vector table MOV @R5,R1 ;R1 -> Handler entry point ADD #,R1 ;R1 -> Extended region name MOV R3,-(SP) ;Save R3 across call CALL UB.RLS(R3) ;Release the extended region UMR's MOV (SP)+,R3 ;Restore R3 for another call MOV @R5,R1 ;R1 -> Handler entry point ADD #,R1 ;R1 -> Handler data base name CALL UB.RLS(R3) ;Release the handler UMR CLC ;Indicate success RETURN ; LWAIT ; Used by load code to await completion of a port command ; issued to the DEUNA ; ; Call: ; R0 -> DEUNA device register base LWAIT: .Assume UN$CS0 EQ 0 .Assume C0.IS EQ 200 TSTB @R0 ;Command complete? BPL LWAIT ;Not yet, wait for it... MOVB UN$CS0+1(R0),UN$CS0+1(R0) ;Yes, now clear the completion bits RETURN .ASSUME <. - LODOVR> LE 1000 MESSAGE= .SBTTL INIT - OnceOnly Initialization Code ;+ ; ; INIT ; Called from Ethernet CLASS handler. ; ; * Note * ; Function originally served by this code has been replaced ; by code which executes at LOAD-time. This remains simply ; as a stub because the routine name is required by the ; CLASS handler. ; ; No longer simply a stub... ; ;- .PSECT NIDVR INIT: MOV @#SYSPTR,R0 ;R0 -> $RMON MOV P1EXT(R0),R0 ;R0 -> PAR1 externalization routine ADD #BLKMOV,R0 ;R0 -> BLKMOV routine MOV R0,$BLKMV ;Save it for later CLC ;Indicate channel is on RETURN ; and return to CLASS handler .SBTTL ENABLE - Enables Interrupts ;+ ; ; ENABLE ; Entered as a result of the first ALLOCATE UNIT request. Causes ; data structures to be built, commands the DEUNA to enter the ; RUNNING state, then enables interrupts. ; ; Returns: ; c-bit = 0, port running ; c-bit = 1, port not running ; ;- .PSECT NIDVR ENABLE: CALL RESRNG ;Build the required data structures MOV #-1,XMITFG ;Ensure transmit and MOV #-1,RECVFG ; receive processes are available MOV NICSR,R0 ;R0 -> DEUNA device register base .Assume UN$CS0 EQ 0 MOVB #PC.STA,@R0 ;Command = Start CALL ISWAIT ;Wait for command completion ;;;error check? MOV UN$CS1(R0),-(SP) ;Get PCSR1 contents BIC #^C,(SP) ;Strip to port status CMP (SP)+,#PS.RUN ;Is the port running? BNE 10$ ;Nope... .Assume UN$CS0 EQ 0 MOVB #,@R0 ;Command = No-op, enable interrupts TST (PC)+ ;Good return, carry = 0 10$: SEC ;Error return, carry = 1 RETURN .SBTTL DISABL - Disable Interrupts ;+ ; ; DISABL ; Entered as a result of the last DEALLOCATE UNIT request. Disables ; interrupts, then commands the DEUNA to enter the READY state. ; ;- .PSECT NIDVR DISABL: MOV NICSR,R0 ;R0 -> DEUNA device register base .Assume UN$CS0 EQ 0 MOVB #PC.NOP,@R0 ;Command = No-op, disable interrupts .Assume UN$CS0 EQ 0 MOVB #PC.STO,@R0 ;Command = Stop CALL ISWAIT ;Wait for command competion ;;;error check? RETURN .SBTTL RESRNG - Reset Ring Structures ;+ ; ; RESRNG ; Builds the data structures known as buffer descriptor lists (BDLs) ; which define the location, size and status of each receive and ; transmit buffer used by the DEUNA. ; ;- .ENABL LSB RESRNG: CALL SAV30 ;We need some scratch registers CALL SAVPAR ; as well as the use of PAR1 .ADDR #RCVBDL,R1 ;R1 -> Receive buffer descriptor list .ADDR #RBFATB,R2 ;R2 -> Receive buffer address table MOV #RBUFCT,R3 ;R3 = Count of receive buffers 10$: .Assume RD.LEN EQ 0 MOV #RBUFSZ,(R1) ;Set the receive buffer size MOV (R2)+,RD.ADL(R1) ;Set the lo-order buffer address MOV (R2)+,R0 ;R0 = Hi-order buffer address BIS #AH.OWN,R0 ;Ownership = DEUNA MOV R0,RD.ADH(R1) ;Place it in the descriptor CLR RD.STA(R1) ;Reset the descriptor status ADD #RD.ESZ,R1 ;On to the next buffer descriptor SOB R3,10$ CLR RCVIDX ;Reset descriptor index .Assume XMTBDL EQ RCVBDL+ .Assume XBFADD EQ RBFATB+<*2> MOV (R2)+,TD.ADL(R1) ;Set the lo- MOV @R2,TD.ADH(R1) ; and hi-order buffer address MOV UDBMTB+2,@#KISAR1 ;Map to the Unibus Data Block MOV UDBMTB,R0 ; and access it using R0 MOV XBDLAD,(R0)+ ;Place the lo- MOVB XBDLAD+2,(R0)+ ; and hi-order address of the ; transmit BDL in the UDB MOVB #,(R0)+ ;Place the entry size MOV #XBUFCT,(R0)+ ; and transmit buffer count ; in the UDB MOV RBDLAD,(R0)+ ;Place the lo- MOVB RBDLAD+2,(R0)+ ; and hi-order address of the ; receive BDL in the UDB MOVB #,(R0)+ ;Place the entry size MOV #RBUFCT,(R0)+ ; and receive buffer count ; in the UDB ; Now lets inform the DEUNA as to where the data structures are MOV UDBADD,PCB+PCB.F2 ;Place lo- MOV UDBADD+2,PCB+PCB.F4 ; and hi-order UDB address in PCB MOV #PF.WRF,PCB+PCB.F0 ;Function = Write Ring Format MOV NICSR,R0 ;R0 -> DEUNA device register base .Assume UN$CS0 EQ 0 MOVB #PC.GC,@R0 ;Command = Get Command CALL ISWAIT ;Wait for command completion ;;;error check? RETURN .DSABL LSB .SBTTL SETUP - Update Address Filtering ;+ ; ; SETUP ; Entered as a result of a change in the unit address table (NIUAT). ; Builds, into the Unibus Data Block (UDB), an updated list of ; multicast addresses this station is to respond to and passes it ; to the DEUNA. ; ;- .PSECT NIDVR .ENABL LSB SETUP: CALL SAV30 ;Save some registers CALL SAVPAR ; and PAR1 context MOV UDBMTB+2,@#KISAR1 ;Map PAR1 to the unibus data block ; where we'll build the table MOV UDBMTB,R1 ;R1 -> Unibus data block .ADDR #NIBROD,R0 ;R0 -> Broadcast address entry MOV #UA.TSZ+1,R2 ;Count of address entries in NIUAT ; plus broadcast entry CLR R3 ;Reset multicast address list count 10$: MOV (R0)+,-(SP) ;Is this entry filled? BIS (R0)+,(SP) BIS (R0)+,(SP)+ BEQ 20$ ;Nope... SUB #UA.ESZ,R0 ;Back up pointer to start of entry MOV (R0)+,(R1)+ ;And move it to the table MOV (R0)+,(R1)+ MOV (R0)+,(R1)+ INC R3 ;Count it CMPB R3,MAXMLT ;Nope, too many addresses for device? BGT 50$ ;Yep... 20$: DEC R2 ;More entries to move? BGT 10$ ;Yes... MOV UDBADD,PCB+PCB.F2 ;Set lo-order UDB address MOVB UDBADD+2,R5 ;R5 = Hi-order UDB address SWAB R3 ;Count of entries in high byte BIS R3,R5 ;Merge with hi-order UDB address MOV R5,PCB+PCB.F4 ; and place it in the PCB MOV #PF.WMA,PCB+PCB.F0 ;Function = Write Multicast Address MOV NICSR,R0 ;R0 -> DEUNA device register base .Assume UN$CS0 EQ 0 MOVB #PC.NOP,@R0 ;Command = No-op, disable interrupts .Assume UN$CS0 EQ 0 MOVB #PC.GC,@R0 ;Command = Get Command CALL ISWAIT ;Wait for command completion ;;;error check? CLR R5 ;Reset the modes we'll be setting TST NIPMFG ;Promiscuous mode? BEQ 30$ ;Nope... BIS #F2.PM,R5 ;Yes, set it 30$: TST NIAMFG ;All Multicast? BEQ 40$ ;Nope... BIS #F2.AM,R5 ;Yes, set it 40$: BIS #,R5 ;Also, disable maintenance mode, ; enable half-duplex mode MOV R5,PCB+PCB.F2 ;Place the modes we want in the PCB MOV #PF.WM,PCB+PCB.F0 ;Function = Write Mode .Assume UN$CS0 EQ 0 MOVB #PC.GC,@R0 ;Command = Get Command CALL ISWAIT ;Wait for command completion ;;;error check? .Assume UN$CS0 EQ 0 MOVB #,@R0 ;Command = No-op, enable interrupts .Assume UN$CS0 EQ 0 MOVB #,@R0 ;Command = Polling Demand TST (PC)+ ;Good return, carry = 0 50$: SEC ;Error return, carry = 1 RETURN .DSABL LSB .SBTTL ISWAIT - Wait for Command Completion ;+ ; ; ISWAIT ; Used to wait for command completion by checking the Interrupt ; Summary (IS) bit in the CSR. ; ; Call: ; R0 -> DEUNA Device Register Base ; ;- ISWAIT: .Assume UN$CS0 EQ 0 .Assume C0.IS EQ 200 TSTB @R0 ;Command done? BPL ISWAIT ;Nope... MOVB UN$CS0+1(R0),UN$CS0+1(R0) ;Yes, clear the completion bits RETURN ; and return .SBTTL NUABRT - Port Handler Abort Code ;+ ; ; NUABRT ; Entered via job or channel abort or by HRESET. Performs ; port-related abort and then calls the class handler abort ; code. ; ;- .PSECT NIDVR NUABRT: CALLR NIABRT ;Pass control to class handler .SBTTL NIINT - Interrupt Entry Point ;+ ; ; NIINT ; Interrupt entry point. Determines what interrupt occurred ; and dispatches to the appropriate routine. ; ; Dispatches with: ; R4 = CSR contents ; R5 -> DEUNA device register base ; ; ** Note ** ; It is the responsibility of routines called by this dispatcher ; to save/restore all registers they will use. ; ;- .PSECT NIDVR .ENABL LSB .DRAST NI,NI$PRI,NUABRT ;;; .FORK NUFBLK ;Following code may take some time MOV NICSR,R5 ;R5 -> DEUNA device register base .Assume UN$CS0 EQ 0 MOV (R5),R4 ;R4 = CSR contents MOVB UN$CS0+1(R5),UN$CS0+1(R5) ;Interrupt bits are write one ; to clear BIT #C0.RXI,R4 ;Have we received something? BEQ 20$ ;Nope... ADD #1,NIIPKT+2 ;Count interrupt ;016 ADC NIIPKT ;016 CALL NUIINT ;Yes, process available packets 20$: BIT #C0.TXI,R4 ;Has a transmit completed? BEQ 30$ ;Nope... ADD #1,NIOPKT+2 ;Count interrupt ;016 ADC NIOPKT ;016 CALL NUOINT ;Yes, process current and start next 30$: BIT #C0.BUI,R4 ;Receive buffer unavailable interrupt? BEQ 40$ ;Nope... .Assume UN$CS0 EQ 0 MOVB #,(R5) ;Command = Polling Demand 40$: RETURN .DSABL LSB .SBTTL NUIINT - Routine to Process Receive Interrupts ;+ ; ; NUIINT ; Entered from interrupt dispatcher. Called to process any ; ethernet frames which have been received. ; ; RECV ; Entered from I/O initialization level after a new receive ; request has been placed on the internal receive queue. ; ;- .PSECT NIDVR .ENABL LSB NUIINT: TST NIQCHG ;Is queue being altered? BNE. 160$ ;Yes, defer processing until later RECV: INC RECVFG ;Allocate the receive process BNE. 160$ ;Can't, its already in use CALL SAV30 ;Save the general registers CALL SAVPAR ; and the current PAR1 context MOV R4,-(SP) ; as well as some additional MOV R5,-(SP) ; registers 10$: CALL GETADR ;Get address of buffer descriptor BIT #AH.OWN,RD.ADH(R1) ;Do we own this descriptor? BEQ 13$ ;Yes ;016 11$: DEC RECVFG ;If there is another frame ;016 BPL 10$ ;to process then loop ;016 JMP 150$ ;Nope... ;016 12$: TST NIQUEF ;Donot throw away frames ;016 BNE 11$ ;if frame queueing is enabled ;016 JMP 130$ ;016 ; Here once we've identified a buffer which the DEUNA has completed 13$: BIT #AH.ERS,RD.ADH(R1) ;Yes, any errors? ;016 BNE. 130$ ;Yes, requeue the packet ; Now we have a packet which is good MOV NIICQE,R4 ;Are there any pending receives? BEQ 12$ ;Nope, check frame queueing ;016 TST NIPMFG ;Promiscuous mode? BNE 110$ ;Yes, don't do protocol/address check MOV RCVIDX,R2 ;R2 = BDL entry index ASL R2 ;R2 = Offset into RBFMTB ASL R2 ; (RBFMTB entries are two words) .ADDR #RBFMTB,R2,ADD ;R2 -> RBFMTB entry for this buffer MOV (R2)+,R0 ;R0 -> Received buffer in handler MOV (R2)+,@#KISAR1 ; extended region (map using PAR1) ; Does someone at this station desire a packet with this protocol .ADDR #NIUPT,R3 ;R3 -> Unit protocol table MOV R3,-(SP) ;Save the base address for later MOV #UP.TSZ,R2 ;R2 = Count of units to check 20$: .Assume UO.ESZ EQ UP.ESZ TST NIUOT-NIUPT(R3) ;Is this unit open? BEQ 30$ ;Nope... CMP EF.TYP(R0),(R3) ;Does this unit want it? BEQ 40$ ;Yes... 30$: TST (R3)+ ;Nope, on to next unit DEC R2 ;More units to check? BGT 20$ ;Yep... TST (SP)+ ;Nope, discard protocol table address JMP 130$ ;No one wants it, ;016 ;so requeue buffer ;016 40$: MOV R3,R2 ;R2 -> Entry in protocol table SUB (SP)+,R3 ;R3 = Protocol table offset .Assume UP.ESZ EQ 2 ASR R3 ;R3 = Table entry .Assume UO.ESZ EQ UP.ESZ ADD #NIUOT-NIUPT,R2 ;R2 -> Corresponding entry in NIUOT .Assume UO.JOB EQ 0 .Assume UO.OFG EQ 1 TST (R2) ;But is the unit open? BEQ 130$ ;Nope (?!), requeue buffer BIT #1,EF.DST(R0) ;Is frame multicast? BEQ 90$ ;Nope, bypass multicast check .ADDR #NUADDR,R4 ;R4 -> Address check table MOV R4,-(SP) ;Save its address MOV R3,-(SP) ;Save table entry for awhile .ADDR #NIPHAD,R3 ;R3 -> Station physical address MOV #*2,R5 ;R5 = Count of words to move ; (Physical and broadcast addresses) 50$: MOV (R3)+,(R4)+ ;Move a word of address DEC R5 ;More to move? BGT 50$ ;Yes... MOV (SP)+,R3 ;R3 = Table entry MUL #UA.ESZ,R3 ;R3 = Address table offset .ADDR #NIUAT,R3,ADD ;R3 -> Address table entry MOV #,R5 ;R5 = Count of words to move ; (Multicast address) 60$: MOV (R3)+,(R4)+ ;Move a word of address DEC R5 ;More to move? BGT 60$ ;Yep... MOV (SP)+,R3 ;R3 -> Base of address check table MOV #3,R4 ;Count of addresses to check ; (Physical, broadcast, multicast) 70$: CMP EF.DST+4(R0),4(R3) ;Match in lo-order address word? BNE 80$ ;Nope... CMP EF.DST+2(R0),2(R3) ;How about in the middle word? BNE 80$ ;Nope... .Assume EF.DST EQ 0 CMP (R0),(R3) ;Now about hi-order word? BEQ 90$ ;Yep... 80$: ADD #UA.ESZ,R3 ;Nope, on to next address DEC R4 ;More addresses to check? BGT 70$ ;Yep... BR 130$ ;Nope, requeue the buffer 90$: .ADDR #NIICQE-Q$LINK,R4 ;Set to check internal receive queue 100$: MOV Q$LINK(R4),R4 ;Follow link to next element BEQ 12$ ;No - check frame queueing ;016 CMPB (R2),Q$UNIT(R4) ;Should we return buffer to this job? BNE 100$ ;Nope... 110$: MOV RD.STA(R1),R5 ;Get status word contents BIC #^C,R5 ;Strip to <11:00>, message length SUB #4,R5 ;Don't count packet CRC MOV R5,-(SP) ;Set to return actual frame length ADD #2,Q$BUFF(R4) ; in word following status word ;;; call fixpar ;Normalize the Qelement CALL @$PTWRD ;Store it SUB #4,Q$BUFF(R4) ;Correct altered BUFF pointer ;;; call fixpar ;Normalize the Qelement INC R5 ;In case of odd-sized frame ASR R5 ;Convert from byte to word count MOV Q$WCNT(R4),R2 ;R2 = Requested length SUB #2,R2 ;Not counting status and size words CMP R5,R2 ;Enough room for entire frame? BLE 120$ ;Yes... MOV #RC.TRU,-(SP) ;Nope, code = 'Truncation' CALL @$PTWRD SUB #2,Q$BUFF(R4) ;Correct altered BUFF pointer ;;; call fixpar ;Normalize the Qelement BIS #HDERR$,@Q$CSW(R4) ;Set the error bit MOV R2,R5 ;And use what is available 120$: MOV RCVIDX,R2 ;R2 = BDL entry index ASL R2 ASL R2 ;R2 = Offset to RBFMTB entry .ADDR #RBFMTB,R2,ADD ;R2 -> RBFMTB entry for this buffer MOV 2(R2),R1 ;R1 = Ethernet frame buffer PAR1 MOV (R2),R2 ;R2 = Ethernet frame buffer offset MOV Q$PAR(R4),R3 ;R3 = User's buffer PAR1 MOV R4,-(SP) ;Save Qelement pointer MOV Q$BUFF(R4),R4 ;R4 = User's buffer offset ADD #4,R4 ; (accounting for status and frame ; length words) CALL @$BLKMV ;Let the monitor move the data MOV (SP)+,R4 ;R4 -> Qelement which just completed MOVB Q$UNIT(R4),R0 ;Update stats ;016 BIC #^C,R0 ;016 ASL R0 ;016 ASL R0 ;016 .ADDR #NIRUN0,R0,ADD ;016 ADD #1,2(R0) ;016 ADC (R0) ;016 CALL NIIDEQ ;Dequeue it and return it to RT ; with our blessing ; Here to requeue a buffer which we have finished processing 130$: CALL GETADR ;Get address of buffer descriptor CLR RD.STA(R1) ;Reset its status MOVB #,RD.ADH+1(R1) ;Owner = DEUNA MOV RCVIDX,R1 ;R1 = Buffer descriptor index INC R1 ;R1 = R1 + 1 mod RBUFCT CMP R1,#RBUFCT BLT 140$ SUB #RBUFCT,R1 140$: MOV R1,RCVIDX ;Save new buffer descriptor index JMP 10$ ;And go process another buffer ; Here when we have processed all buffers owned by us 150$: MOV (SP)+,R5 ;Restore specifically saved registers MOV (SP)+,R4 ; (Others will be restored during ; stack unwind) MOV #-1,RECVFG ;Deallocate the receive process ;;; .Assume UN$CS0 EQ 0 ;;; MOVB #,@NICSR ;Inform DEUNA of available buffers 160$: RETURN .DSABL LSB .SBTTL GETADR - Get Buffer Descriptor Address ;+ ; ; GETADR ; Determines the address of the buffer descriptor to be ; processed next (descriptors can be envisioned as being ; in a ring). ; ; Implicit input: ; RCVIDX = Index into buffer descriptor list ; ; Returns: ; R1 -> Receive buffer descriptor ; ;- GETADR: MOV RCVIDX,R1 ;R1 = Buffer descriptor index MUL #RD.ESZ,R1 ;R1 = Descriptor offset .ADDR #RCVBDL,R1,ADD ;R1 -> Buffer descriptor RETURN .SBTTL NUOINT - Routine to Process Transmit Interrupts ;+ ; ; NUOINT ; Entered from interrupt dispatcher. Called when transmit ; has completed. ; ; XMIT ; Entered from DRBEG code after a new Qelement has been placed ; on the internal transmit queue. ; ; ** Note ** ; In order to avoid an NU-only restriction, a frame to be ; transmitted is first moved to the transmit buffer in the ; handler extended memory region and is transmitted from ; there. This is due to the fact that the DEUNA, as a ; unibus device, is limited to 18-bit DMA unless the unibus ; map is empoyed. ; ;- .PSECT NIDVR .ENABL LSB NUOINT: CALL SAV30 ;Save some registers CALL SAVPAR ; as well as current PAR1 context MOV R4,-(SP) ; and some additional registers MOV R5,-(SP) .ADDR #XMTBDL,R5 ;R5 -> Transmit buffer descriptor MOV #-1,XMITFG ;Deallocate the transmit process MOV NIOCQE,R4 ;R4 -> Current Qelement BEQ 70$ ;None (?!)... MOV Q$LINK(R4),NIOCQE ;Link the internal queue forward CLR Q$LINK(R4) ;Ensure this doesn't link anywhere BIT #AH.ERS,TD.ADH(R5) ;Did the transmit fail? BEQ 40$ ;Nope... ; Here if the transmit failed BIT #ST.LCA,TD.STA(R5) ;Failed due to carrier loss? BEQ 10$ ;Nope... MOV #,-(SP) ;Code = 'Transmit failed' ;Subcode = 'Carrier check' BR 30$ ;Use common error reporting code 10$: BIT #ST.RTY,TD.STA(R5) ;Failed due to excessive collisions? BEQ 20$ ;Nope... MOV #,-(SP) ;Code = 'Transmit failed' ;Subcode = 'Excessive collisions' BR 30$ ;Use common error reporting code 20$: MOV #,-(SP) ;Code = 'Transmit failed' ;Subcode = Unknown .BR 30$ ;Use common error reporting code ; Common error reporting point ; R4 -> Qelement (at Q$BLKN) ; (SP) = Status code to be returned 30$: BIS #HDERR$,@Q$CSW(R4) ;Set the hard error bit CALL @$PTWRD ;Set the status ; Common request completion point ; R4 -> Qelement (at Q$BLKN) 40$: MOVB Q$UNIT(R4),R0 ;Update stats ;016 BIC #^C,R0 ;016 ASL R0 ;016 ASL R0 ;016 .ADDR #NIXUN0,R0,ADD ;016 ADD #1,2(R0) ;016 ADC (R0) ;016 CALL NIFIN ;Complete the request ;016 BR 50$ ;Bypass some redundant saves ; Here we check for something to transmit XMIT: CALL SAV30 ;Save some registers CALL SAVPAR ; as well as current PAR1 context MOV R4,-(SP) ; and some additional registers MOV R5,-(SP) 50$: TST NOQCHG ;Is queue being altered? BNE 70$ ;Yes, wait to be entered via XMIT MOV NIOCQE,R4 ;Anything queued for transmit? BEQ 70$ ;Nope... INC XMITFG ;Yes, allocate the transmit process BNE 70$ ;Can't, its already in use MOV Q$PAR(R4),@#KISAR1 ;Map to the Ethernet frame in MOV Q$BUFF(R4),R2 ; the user's buffer using PAR1 ADD #<4+EF.SRC>,R2 ;R2 -> Ethernet source address field ; (bypass status and reserved words) MOV (R2),-(SP) ;Has source been specified? BIS 2(R2),(SP) BIS 4(R2),(SP)+ BNE 60$ ;Yes, don't touch it... .ADDR #NIPHAD,R1 ;R1 -> Station physical address MOV (R1)+,(R2)+ ;Move the physical address into MOV (R1)+,(R2)+ ; the source field of the frame MOV (R1)+,(R2)+ ; to be transmitted 60$: MOV Q$PAR(R4),R1 ;R1 = User's buffer PAR1 MOV Q$BUFF(R4),R2 ;R2 = User's buffer offset ADD #4,R2 ; (bypass status and reserved words) MOV Q$WCNT(R4),R5 ;R5 = Count of words to transfer SUB #2,R5 ; (minus status and reserved words) MOV R5,-(SP) ;Save it awhile MOV XBFMTB+2,R3 ;R3 = Transmit buffer PAR1 MOV XBFMTB,R4 ;R4 = Transmit buffer offset CALL @$BLKMV ;Let the monitor move the data .ADDR #XMTBDL,R0 ;R0 -> Transmit buffer descriptor ASL (SP) ;Convert from words to bytes .ASSUME TD.LEN EQ 0 MOV (SP)+,@R0 ;Set the frame size CLR TD.STA(R0) ;Reset the status ; port-driver MOVB #</400>,TD.ADH+1(R0) ;Ownership = DEUNA, ; one and only descriptor MOVB #,@NICSR ;Command = Polling Demand 70$: MOV (SP)+,R5 ;Restore specifically saved registers MOV (SP)+,R4 ; (others will be restored during ; stack unwind) RETURN .DSABL LSB .SBTTL Port Handler Inpure Data Area .PSECT NIDAT NINAME: .WORD NI$HND ;Extended memory handler region name .RAD50 /$ / $BLKMV: .BLKW ; : Address of RMON BLKMOV routine ; The Port Control Block (PCB) is a data structure used by the port ; driver to request functions of the DEUNA. PCB: .WORD PF.RDA ;Port control block (preset for .WORD 0,0,0 ; 'read default address' command) ; Buffer address/PAR tables. Each table consisting of two-word entries ; which contain the normalized address and PAR1 values which are used ; to access the receive and transmit buffers as well as the Unibus Data ; Block (UDB), all located in the handler extended memory region. ; ; Note that since the values have been normalized, and since the buffers ; are not larger than a PAR, it is possible to address an entire buffer ; without crossing a PAR boundary. ; ** Begin Critical Ordering ** RBFMTB: .BLKW ;Address/PAR values for receive XBFMTB: .BLKW 2 ; and transmit buffers UDBMTB: .BLKW 2 ; and UDB ; ** End critical ordering ** ; Address tables. Each table consists of two-word entries which contain ; either the 18-bit physical address or the 18-bit UMR address for the ; structure they define. ; ** Begin Critical Ordering ** RBFATB: .BLKW ;Address table for receive buffers XBFADD: .BLKW 2 ;Address of transmit buffer UDBADD: .BLKW 2 ;Address of Unibus Data Block ; ** End Critical Ordering ; ** Begin Critical Ordering PCBADD: .BLKW 2 ;Address of Port Control Block RBDLAD: .BLKW 2 ;Address of receive BDL XBDLAD: .BLKW 2 ;Address of transmit BDL ; ** End critical ordering ** ; ** Begin Critical Ordering ** RCVBDL: .BLKB ;Receive descriptor ring XMTBDL: .BLKB TD.ESZ ;Transmit descriptor ; ** End critical ordering ** RCVIDX: .BLKW ;Index to next receive buffer to ; process RECVFG: .WORD -1 ; : 'Receive process available' flag XMITFG: .WORD -1 ; : 'Transmit process available' flag FQELEM: .BLKW 6 ;Fake Qelement (enough to contain ; Q$BLKN to Q$PAR equivalents) NUADDR: .BLKW 3*UA.ESZ ;Address check table NUFBLK: .WORD 0,0,0,0 ;Fork block for interrupt processing MAXMLT: .WORD 0 ; : Maximum multicast list size NUHNAM: .RAD50 /NUH/ ;For handler extended region NULNAM: .RAD50 /NUL/ ;For handler data bas NISLEN =: <.-NISBLK>/2 ;016 .SBTTL Installation code overlay ;+ ; ; INSOVR ; Installation code overlay. Loaded and entered via code ; residing in block 0 of the handler. Initiates a reset ; which may cause self-test to be executed if the board ; hasn't previously passed self-test. The reason DNI is ; examined for completion rather than C0.IS is that during ; self-test, all other PCSR0 bits are altered. ; ; Note: ; o If UB is active, then the handler extended region may ; reside anywhere in memory, since the UMR support will ; handle DMA. ; ; o The first contribution to the SETOVR PSECT comes from ; the UNLOAD code in NI. ; ;- .PSECT SETOVR . = LODOVR + 1000 ;Align to next block OVRBK0 =: . ;Base for this overlay .ENABL LSB INSOVR: MOV @#SYSPTR,R1 ;R1 -> $RMON BIT #,CONFG2(R1) ;Running on a PRO or QBUS machine? BNE. I.ERR ;Yes, reject installation MOV +INSCSR,R2 ;R2 -> DEUNA device register base .Assume UN$CS0 EQ 0 MOV #C0.RSE,@R2 ;Command = Reset 10$: .IF NE TIM$IT CLR ITMOFG ;Reset the timeout flag .ADDR #ITAREA+10,R0 ;R0 -> word beyond .MRKT request block MOV #177000+NI$COD,-(R0) ;Set ID, NU handler ID .ADDR #ITCOMP,-(SP) ;(SP) -> Timer completion routine MOV (SP)+,-(R0) ;Set it in EMT area .ADDR #ITTBLK,-(SP) ;(SP) -> Timer block MOV (SP)+,-(R0) ;Set it in EMT area MOV #<22*400>,-(R0) ;Finally, EMT subcode .MRKT CODE=NOSET ;Post the timer BCS I.ERR ;In case of error... .ENDC ;NE TIM$IT 20$: CMP @R2,# ;Has self-test failed completely? BEQ I.ERR ;Yes... .Assume UN$CS0 EQ 0 BIT #C0.DNI,@R2 ;DNI set? .IF EQ TIM$IT BEQ 20$ ;Not yet, wait for it... .IFF ;EQ TIM$IT BNE 30$ ;Yep... TST ITMOFG ;Nope, has the timer gone off? BEQ 20$ ;Not yet... BR I.ERR ;Yes, reject the installation .ENDC ;EQ TIM$IT 30$: .IF NE TIM$IT .ADDR #ITAREA+6,R0 ;R0 -> word beyond .CMKT request block CLR -(R0) ;Don't need returned time MOV #177000+NI$COD,-(R0) ;Set ID, NU handler ID code MOV #<23*400>,-(R0) ;Finally, the EMT subcode .CMKT CODE=NOSET ;Cancel the outstanding timer BCS I.ERR ;In case of error... .ENDC ;NE TIM$IT MOVB UN$CS0+1(R2),UN$CS0+1(R2) ;Yes, now clear the completion bits ;;;error check? MOVB UN$CS1(R2),-(SP) ;Get PCSR1 contents BIC #^C,(SP) ;Strip to port status CMPB (SP)+,#PS.RDY ;Is the port ready? ; (Should be READY after RESET) BNE I.ERR ;No, so reject installation ASL #100000 ;First time here? BCC 40$ ;Nope, bypass some code .Assume UN$CS0 EQ 0 MOVB #PC.ST,@R2 ;Force a self-test (in case ; it previously passed) BR 10$ ; Now we allocate a region in extended memory for our use 40$: MOV P1EXT(R1),R0 ;R0 -> PAR1 Externalization routine 50$: .ADDR #NUNAME,R5 ;R5 -> Region name to search for CALL FINDGR(R0) ;Check for existing region BCS 70$ ;None exists, try to create it MOV R1,-(SP) ;Save pointer to RCB BIT #GR.NRF,GR.STA(R1) ;Return memory to free list? BNE 60$ ;Nope... MOV GR.SIZ(R1),R2 ;R2 = Size of region to deallocate MOV GR.ADR(R1),R1 ;R1 -> Region CALL XDEALC(R0) ;Deallocate the region 60$: CLR @(SP)+ ; and free the RCB (size = 0) BR 50$ ;Loop until region not found 70$: MOV @#SYSPTR,R1 ;R1 -> $RMON MOV MEMPTR(R1),R0 ;R0 = Offset to table of offsets ADD R1,R0 ;R0 -> Table of offsets MOV CORPTX(R0),R0 ;R0 = Offset to free memory list ADD R1,R0 ;R0 -> Extended memory free list CLR -(SP) ;Reset 'best-fit' entry pointer MOV #-1,-(SP) ; and size 80$: CMP (R0),#-1 ;End of free list? BEQ 110$ ;Yes, no memory for us... TST (R0) ;Does this entry describe any memory? BEQ 100$ ;Nope... CMP (R0),#EMEMSZ ;Large enough for what we need? BLO 100$ ;Nope... ;;; BIT #CF3.UA,$CNFG3(R1) ;Is UB active? ;;; BNE 90$ ;Yes, so region can be anywhere MOV 2(R0),-(SP) ;Get region address ADD #EMEMSZ,(SP) ;Add size we need CMP (SP)+,#10000 ;Will it span 18-bit boundary? BHI 100$ ;Yes, we can't use it 90$: CMP (R0),(SP) ;Does this region suit us best? BHIS 100$ ;Nope... MOV R0,2(SP) ;Yes, save a pointer to its entry MOV (R0),(SP) ; as well as its size 100$: ADD #4,R0 ;On to next region BR 80$ 110$: TST (R0)+ ;Bump to first word of GRCB table MOV R0,R2 ;Copy it for GRCB table scan TST (SP)+ ;Discard best fit size MOV (SP)+,R0 ;Get best fit entry pointer BEQ I.ERR ;No fit... 120$: CMP (R2),#-1 ;End of GRCB table? BEQ I.ERR ;Yes, can't allocate region TST (R2) ;Is this GRCB free? BEQ 130$ ;Yep, go use it... ADD #GR.ESZ,R2 ;Nope, on to next BR 120$ 130$: MOV #EMEMSZ,(R2)+ ;Build GRCB (size) SUB #EMEMSZ,(R0)+ ;Reduce free region by what we use MOV (R0),(R2)+ ; (address) ADD #EMEMSZ,(R0) ;Adjust free region start address MOV #GR.PVT,(R2)+ ; (status) MOV NUNAME,(R2)+ ; (name, word 1) MOV NUNAME+2,(R2)+ ; (name, word 2) I.NORM: TST (PC)+ ;Normal return, accept installation I.ERR: SEC ;Error return, reject installation RETURN NUNAME: .WORD NI$HND .RAD50 /$ / ; Timer completion for install self-test .IF NE TIM$IT ITCOMP: MOV SP,ITMOFG ;Set the timeout flag RETURN ITMOFG: .WORD 0 ; : Timeout flag ITTBLK: .WORD 0,16.*60. ; : Timer block, 16 seconds ITAREA: .BLKW 4 ;EMT area for .MRKT/.CMKT .ENDC ;NE TIM$IT .DSABL LSB .Assume <.-OVRBK0> LE 1000 MESSAGE= .SBTTL Set Code Overlay ;+ ; ; Set code overlay. Loaded and entered via code residing in block ; 0 of the handler. ; ; If the handler is not loaded, the stations default physical address ; is obtained by issuing a READ DEFAULT ADDRESS port command. If ; the handler is loaded, such a command may not be issued because of ; possible contention for location and use of the Port Control Block ; (PCB), so SET code operates as any other Ethernet program, opening ; a channel to the handler, allocating a unit (which returns the ; stations physical address), deallocating the unit and closing the ; channel. ; ;- .PSECT SETOVR . = OVRBK0 + 1000 ;Align to next block OVRBK1 =: . ;Base for this overlay ; SET NU SHOW .MCALL .DSTAT, .LOOKU, .SPFUN .MCALL .TRPSE, .PRINT, .TTYOU .MCALL .CLOSE, .PURGE ..DSTA =: 342 ;.DSTATUS ..LKUP =: 375 ;.LOOKUP ..SPFN =: 375 ;.SPFUN .ENABL LSB O.SHOW: .ADDR #OVRBK1,R0 ;Get overlay load address ADD R0,LKAREA+2 ;Relocate address of device block ADD R0,SPAREA+4 ; and spfun buffer .ADDR #DBLK,R0 ;R0 -> Device name .ADDR #DSAREA+1,-(SP) ;(SP) -> .DSTATUS information block EMT ..DSTA ;*** .DSTATUS *** BCS 10$ ;Device not found, try direct method TST DSAREA+4 ;Is handler loaded? BEQ 10$ ;Nope, so use direct method CALL DEVTYP ;Determine device type .PURGE #0 ;Ensure channel is available to us .ADDR #LKAREA,R0 ;R0 -> EMT area for .LOOKUP EMT ..LKUP ;*** .LOOKUP *** BCS S.ERR ;Lookup error... .ADDR #SPAREA,R0 ;R0 -> EMT area for .SPFUN EMT ..SPFN ;*** .SPFUN *** (allocate unit) ROL -(SP) ;Save the carry .ADDR #SPAREA,R0 ;R0 -> EMT area for .SPFUN CLR SPAREA+2 ;Convert to deallocate unit EMT ..SPFN ;*** .SPFUN *** (deallocate unit) .PURGE #0 ;Discard the channel ROR (SP)+ ;Restore the carry BCS S.ERR ;In case of .SPFUN error BR 20$ ;Use common address reporting code 10$: .ADDR #TSAREA,R0 ;R0 -> EMT area for .TRPSET .ADDR #NUNXM,R1 ;R1 -> Our NXM trap routine .TRPSE R0,R1 ;Redirect NXM traps MOV +INSCSR,R0 ;R0 = DEUNA device register base TST @R0 ;Does the hardware exist? NOP ;Some processors trap late BCS S.ERR ;Nope... CALL DEVTYP ;Determine device type .ADDR #SPCB,R1 ;R1 -> Set-time PCB MOV R1,UN$CS2(R0) ;Set the lo-order CLR UN$CS3(R0) ; and hi-order pcb address .Assume UN$CS0 EQ 0 MOVB #PC.GP,@R0 ;Command = Get PCB address CALL SWAIT ;Wait for command completion ;;;error check? MOV #PF.RDA,SPCB+PCB.F0 ;Function = Read Default Address .Assume UN$CS0 EQ 0 MOVB #PC.GC,@R0 ;Command = Get Command CALL SWAIT ;Wait for command completion ;;;error check? 20$: .ADDR #M.PADD,R0 ;Set to print the address message .PRINT ;Do it... .ADDR #SPCB+PCB.F2,R1 ;R1 -> Address MOV #6.,R2 ;R2 = Count of hex pairs to print BR 40$ ;Dive into loop 30$: .TTYOU #'- ;Delimiter 40$: MOVB (R1)+,-(SP) ;Get a byte of the address MOVB (SP),R0 ;Make a copy of it ASR R0 ;Shift down high nybble ASR R0 ASR R0 ASR R0 BIC #^C<17>,R0 ;Strip to just the nybble we want .ADDR #HEX,R0,ADD ;R0 -> Hex character to print .TTYOU @R0 ;Print it MOVB (SP)+,R0 ;Get the address byte again BIC #^C<17>,R0 ;Using the low nybble this time .ADDR #HEX,R0,ADD ;R0 -> Hex character to print .TTYOU @R0 ;Print it DEC R2 ;More to do? BGT 30$ ;Yep... .ADDR #M.CRLF,R0 ;Nope, we'll end with a pair .PRINT S.NORM: TST (PC)+ ;Normal return, carry clear S.ERR: SEC ;Error return, carry set ROL R2 ;Save carry in R2 JMP +GETBK1 ;Restore block 1 .DSABL LSB ; DEVTYP ; Determines device type (DEUNA vs. DELUA) and displays ; the information. ; DEVTYP: MOV R0,-(SP) ;Save R0 first MOV +INSCSR,R0 ;R0 = DEUNA device register base MOV UN$CS1(R0),R0 ;R0 = PCSR1 contents BIC #^C,R0 ;Strip to device ID field BNE 10$ ;If <06:04> = 1, DELUA .ADDR #M.DEUN,R0 ;DEUNA, R0 -> ID string BR 20$ 10$: .ADDR #M.DELU,R0 ;DELUA, R0 -> ID string 20$: .PRINT ;Display the device type MOV (SP)+,R0 ;Restore saved R0 RETURN NUNXM: BIS #1,2(SP) ;Set the carry for return RTI ; and then do so ; SWAIT ; Used by set code to await completion of a port command ; issued to the DEUNA ; ; Call: ; R0 -> DEUNA device register base SWAIT: .Assume UN$CS0 EQ 0 .Assume C0.IS EQ 200 TSTB @R0 ;Command complete? BPL SWAIT ;Not yet, wait for it... MOVB UN$CS0+1(R0),UN$CS0+1(R0) ;Yes, now clear the completion bits RETURN ; Device block for .DSTATUS, .LOOKUP DBLK: .WORD NI$HND ;Device name .WORD 0,0,0 ; Information block for .DSTATUS DSAREA: .BLKW 4 ; EMT area for .LOOKUP LKAREA: .BYTE 0 ; : Channel .BYTE 1 ; : EMT code .WORD DBLK-OVRBK1 ; : Device block *** Relocated *** .WORD 0 ; : Sequence number ; EMT area for .SPFUN SPAREA: .BYTE 0 ; : Channel .BYTE 32 ; : EMT code .WORD 1 ; : Block (spfun modifier) .WORD SPCB-OVRBK1 ; : Buffer *** Relocated *** .WORD 0 ; : Word count .BYTE 377,SP.POR ; : Function (allocate unit) .WORD 0 ; : Wait-mode I/O ; Port Control Block (PCB) for SET SHOW SPCB: .WORD PF.RDA ; : Function (read default address) .WORD 0,0,0 ; EMT area for .TRPSET TSAREA: .BLKW 2 M.DEUN: .ASCII /DEUNA, /<200> M.DELU: .ASCII /DELUA, /<200> M.PADD: .ASCII /Station address = /<200> M.CRLF: .BYTE 0 HEX: .ASCII /0123456789ABCDEF/ .EVEN .ASSUME <.-OVRBK1> LE 1000 MESSAGE= .END