;+ ; ; Ethernet communications test loopback server. ; ; By John Wilson. ; ; Usage: ; ; ECTLOOP /I:60 ; /I -- INT # of ethernet packet driver (default is to search from 20-FF) ; ; 11/19/94 JMBW Created. ; 06/22/97 JMBW Changed to properly use multicast reception instead of ; promiscuous mode (to test out packet driver). ; ;- .radix 8 ; lf= 12 cr= 15 ; timer_low=word ptr 046Ch ;low word of system time ; mcmax= 100d*6 ;max size of multicast list (in bytes) ; code segment assume cs:code org 100h start: cld ;DF=0 mov dx,offset banner ;say hello mov ah,09h ;func=print int 21h ; parse command line mov si,80h ;pt at it lodsb ;get length cbw ;ah=0 mov cx,ax ;copy jcl1: call skip ;skip blanks jnc $+5 jmp jcl7 cmp al,'/' ;switch? je jcl2 cmp al,'-' jne jcl3 jcl2: inc si ;skip it dec cx jcl3: call skip ;get switch char jc jcl7 inc si ;skip the char dec cx and ax,337 ;convert to upper, ah=0 push ax ;save jcxz jcl5 lodsb ;get char cmp al,':' ;: or = je jcl4 cmp al,'=' jne jcl5 jcl4: dec cx ;count it call ghex ;get numeric arg pop di ;restore cmd cmp di,'I' ;nterrupt # of packet driver? je jcl6 jcl5: mov dx,offset usage ;pt at msg mov cx,lusage ;length call stderr ;print it mov ax,4C01h ;func=punt int 21h jcl6: ; /I:nn (set INT vector number of packet driver) mov ds:intno,al ;save jmp jcl1 jcl7: ; done, init packet driver mov al,ds:intno ;get int # of packet driver or al,al ;got one? jz init1 call probe ;give it a try jc init3 jmp short init4 init1: ; check all int #'s in [20h,0FFh] mov al,20h ;starting posn init2: push ax ;save call probe ;give it a try pop ax ;[restore] jnc init4 ;got one inc al ;+1 jnz init2 ;keep going unless it was FF init3: ; driver not found mov dx,offset nodrv ;pt at msg mov cx,lnodrv call stderr ;print on stderr mov ax,4C01h ;func=punt int 21h init4: ; driver attached mov dx,offset ctrlc ;pt at ^C vector mov ax,2523h ;func=set INT 23h vector int 21h ; display our address mov si,offset us ;point at binary address mov di,offset saddr1 ;point at where it goes call paddr ;convert mov byte ptr [di],cr ;PADDR overwrote the CR mov dx,offset saddr ;pt at msg mov ah,09h ;func=print int 21h ;+ ; ; Main loop. Wait for next packet. ; ;- mloop: xor ax,ax ;load 0 into es mov es,ax mov cx,9d ;timeout=approx. 1/2 second mlp1: mov bx,es:timer_low ;get low word of DOS timer mlp2: cmp byte ptr ds:rcvd,0 ;have we received anything? jnz mlp3 cmp bx,es:timer_low ;no, see if timer has changed je mlp2 ;spin if not loop mlp1 ;count the tick mov ah,0Bh ;func=get STDIN status int 21h ;(allow user to ^C) jmp short mloop ;loop mlp3: ; got something mov si,offset buf+14d+2 ;point past skip count add si,[si-2] ;add in count add word ptr [si-2],8d ;(update for looping) lodsw ;fetch function mov dl,'R' ;assume we received a reply dec ax ;=1 (reply)? jz mlp4 ;yes dec ax ;=2 (forward)? jnz mlp5 ;no, drop push ds ;copy DS to ES pop es mov di,offset buf ;set new dest addr movsw movsw movsw mov si,offset us ;pt at our addr movsw ;set new source addr movsw movsw mov si,offset buf ;pt at buffer mov cx,ds:len ;length mov ah,04h ;func=SEND_PKT call pkdrv ;send reply mov dl,'.' ;print . mlp4: mov ah,02h ;func=CONOUT int 21h mlp5: dec ds:rcvd ;clear flag (allow more input) jmp short mloop ;+ ; ; Frame received. Ints may be off on entry. ; ; ax 0 (return CX-byte buf at ES:DI) or 1 (CX-byte buf loaded at DS:SI) ; ds preserved ; ;- ethin: pushf ;save flags cli ;ints off if they weren't test ax,ax ;;second call? jnz ethin2 ;;yes, collect the packet ; buffer request, alloc it cmp byte ptr cs:rcvd,0 ;;in use? jnz ethin1 ; pack driver spec 1.10 or later needed for lookahead: mov ax,cs ;;set segment mov es,ax mov di,offset buf ;;pt at buffer mov cx,1514d ;;length popf retf ethin1: ; drop packet xor di,di ;;load 0:0 xor ax,ax mov es,di popf retf ethin2: ; buf copied, deal with it inc cs:rcvd ;;set flag mov cs:len,cx ;;save length popf retf ;+ ; ; ^C received. ; ;- ctrlc: push cs ;copy CS to DS pop ds mov dx,offset ccmsg ;pt at msg mov cx,lccmsg ;length call stderr cmp byte ptr ds:initf,0 ;initted? jz ctrlc1 call endei ;yes, deinstall ctrlc1: mov ax,4C00h ;func=exit int 21h ;+ ; ; Print a msg on STDERR. ; ; dx ptr ; cx length ; ;- stderr: mov bx,0002h ;handle=stderr mov ah,40h ;func=write int 21h ret ;+ ; ; Check for the existence of a packet driver and install our connection ; to it if it is found. ; ; al int # (in the range [20h,0FFh]) ; ; CF=0 on success. ; ;- probe: push cs ;copy cs pop ds ;to ds mov ds:intno,al ;patch in INT # mov ah,35h ;func=get int vector int 21h mov ax,es ;0000:0000? or ax,bx jz prob1 lea di,[bx+3] ;pt at signature mov si,offset pktdrv ;pt at string mov cx,9d ;length repe cmpsb ;check for signature je prob2 prob1: stc ;error return ret prob2: ; found one get handle mov ax,0201h ;func=ACCESS_TYPE, class=DIX ethernet mov bx,-1 ;board type=any xor dl,dl ;board #0 (only one per int anyway (?!)) mov cx,2 ;len(type)=2 mov si,offset prot ;pt at type mov di,cs ;copy CS to both mov ds,di mov es,di mov di,offset ethin ;receive entry call pkdrv ;call pktd jc prob3 ;punt inc ds:initf ;set flag mov ds:handle,ax ;save handle ; check capabilities mov bx,ax ;in case old driver mov ax,01FFh ;func=DRIVER_INFO call pkdrv ;call pktd jc prob5 ;failed cmp al,2 ;need basic+extended functions for multicast je prob4 cmp al,6 ;basic+high-performance+extended is OK too je prob4 ;no good ; doesn't support extended functions, can't do multicasts mov al,ds:intno ;get vector # mov di,offset notex1 ;point at where it goes call phex mov dx,offset notex ;point at msg mov cx,lnotex call stderr ;print it jmp short prob5 ;close handle, die prob3: jmp short prob6 prob4: ; set receive mode to enable multicasts (requires "extended" funcs) mov cx,4 ;receive stuff addressed to us, b'cast, multi mov bx,ds:handle ;handle mov ah,14h ;func=SET_RCV_MODE (extended driver function) call pkdrv ;call pktd jc prob5 ; get existing multicast address list mov ah,17h ;func=GET_MULTICAST_LIST (extended drvr func) call pkdrv1 ;call pktd, don't preserve ES jc prob5 push ds ;swap DS and ES push es pop ds pop es cmp cx,mcmax-6 ;must be space for one more ja prob5 ;no mov si,di ;point at their list mov di,offset mcbuf ;and our buf rep movsb ;copy into our buf push es ;copy ES back to DS pop ds ; add our multicast address (ECT loopback assist) mov si,offset mcast ;point at it movsw ;copy movsw movsw ; write the whole mess back out mov cx,offset mcbuf ;point at address sub di,cx ;find length xchg cx,di ;ES:DI=ptr, CX=length mov ah,16h ;func=SET_MULTICAST_LIST (extended drvr func) call pkdrv ;call pktd jc prob5 ; get hardware addr mov di,offset us ;pt at HA buf (ES loaded above) mov cx,6 ;ethernet hardware addr length mov bx,ds:handle ;handle mov ah,06h ;func=GET_ADDRESS call pkdrv ;call pktd jnc prob7 ;successful, go return prob5: mov bx,ds:handle ;get handle mov ah,03h ;func=RELEASE_TYPE call pkdrv prob6: stc prob7: ret ;+ ; ; Deinstall our input handler. ; ;- endei: mov bx,ds:handle ;get handle mov ah,03h ;func=RELEASE_TYPE call pkdrv ; get our addr out of multicast list mov ah,17h ;func=GET_MULTICAST_LIST (extended drvr func) call pkdrv1 ;call pktd, don't preserve ES jc endei4 ;nothing we can do about this push ds ;swap DS and ES push es pop ds pop es cmp cx,mcmax ;must fit ja endei4 ;no, no recovery, sorry mov si,di ;point at their list mov di,offset mcbuf ;and our buf mov dx,cx ;copy endei1: sub dx,6 ;more to do? jc endei3 ;no mov ax,di ;save ptrs mov bx,si mov di,offset mcast ;point at our address mov cx,6 ;length repe cmpsb ;is this one ours? je endei2 ;yes, don't copy it (CX=0) mov si,bx ;no, back up source pointer mov cl,6 ;set length again endei2: mov di,ax ;restore dest ptr rep movsb ;copy it, or not if CX=0 from REPE CMPSB jmp short endei1 ;loop endei3: push es ;copy ES back to DS pop ds ; write the whole mess back out mov cx,offset mcbuf ;point at address sub di,cx ;find length xchg cx,di ;ES:DI=ptr, CX=length mov ah,16h ;func=SET_MULTICAST_LIST (extended drvr func) call pkdrv ;call pktd (can't recover from err, so ignore) endei4: ret ; pkdrv: ; do INT instruction to call packet driver push es ;save call pkdrv1 ;call pop es ;restore ret ; pkdrv1: ; do INT but don't preserve ES push ds ;save db 0CDh ;INT nn intno db 0 ;call the packet driver cld ;restore DF pop ds ;restore ret ;+ ; ; Skip blanks, control chars. ; ; Updates SI, CX, returns CF=1 if EOL. Otherwise AL=first non-blank char. ; ;- skip: jcxz skip3 ;eol already skip1: lodsb ;get a byte cmp al,' ' ;blank or ctrl char? jbe skip2 ;yes dec si ;unget, CF=0 already ret skip2: loop skip1 ;loop skip3: stc ;hit eol ret ;+ ; ; Get a hex number (up to 48 bits) ; ; si ptr to input line ; cx # chars left ; ; dx:bx:ax returns number ; ;- ghex: xor di,di ;init # xor bx,bx xor dx,dx ghex1: lodsb ;get a char cmp al,'-' ;ignore embedded '-' je ghex3 cmp al,'0' ;digit? jb ghex4 cmp al,'9' jbe ghex2 ;yes and al,not 40 ;cvt to upper cmp al,'A' ;letter? jb ghex4 cmp al,'F' ja ghex4 sub al,7 ;make A follow 9 ghex2: sub al,'0' ;convert to binary and ax,0Fh ;isolate new digit push cx ;save mov cl,4 ;bit count rol di,cl ;make space rol bx,cl sal dx,cl mov cx,di ;get lowest and di,0FFF0h ;make space for new digit xor cx,di ;isolate old one or di,ax ;OR in new digit mov al,bl ;get middle and bl,0F0h ;make space for new digit xor al,bl ;isolate old one or bl,cl ;OR in new digit or dl,al ;OR in new digi pop cx ;restore ghex3: loop ghex1 ;around for more ghex4: dec si ;unget (or not if off end, doesn't matter) mov ax,di ;get low digits ret ;+ ; ; Put 2-digit hex # in AL into buf at ES:DI, incrementing DI. ; ;- phex: mov ah,al ;copy mov cl,4 ;shift count shr ah,cl ;right 4 and al,0Fh ;isolate mov bx,offset hextab ;pt at table xlat ;translate low nibble xchg al,ah ;swap xlat ;translate high nibble stosw ;save both ret ;+ ; ; Convert ethernet addr at DS:SI to hex at ES:DI. ; ;- paddr: mov cx,6 ;byte count mov dx,di ;copy addr paddr1: lodsb ;get a byte push cx ;save call phex ;print it mov al,'-' ;add '-' stosb pop cx ;restore loop paddr1 ;loop dec di ;unput final '-' mov cx,17d ;length ret ; subttl pure data ; banner db 'ECTLOOP V1.1 Ethernet Communications Test loopback server' db cr,lf db 'By John Wilson ',cr,lf db '$' usage db 'Usage: ECTLOOP [/I:(hex int no of packet driver)]',cr,lf lusage= $-usage ; nodrv db '?Unable to attach to packet driver',cr,lf lnodrv= $-nodrv ; ccmsg db 'Exiting...',cr,lf lccmsg= $-ccmsg ; hextab db '0123456789ABCDEF' ; pktdrv db 'PKT DRVR',0 ;signature to match ; prot db 90h,00h ;prot to look for (ECT=90-00) mcast db 0CFh,0,0,0,0,0 ;loopback assist addr (CF-00-00-00-00-00) ; subttl impure data ; notex db '?Packet driver at INT ' notex1 db 'XX does not support extended driver functions',cr,lf lnotex= ($-notex) ; initf db 0 ;NZ => pktd initted (handle allocated) ; rcvd db 0 ;set NZ when valid packet received ;cleared when buf is once again available ; saddr db 'Station address: ' saddr1 db 'aa-bb-cc-dd-ee-ff' db cr,lf,'$' ;CR gets overwritten by PADDR ; subttl pure storage ; handle dw 1 dup(?) ;handle for our packet type us db 6 dup(?) ;our addr len dw 1 dup(?) ;length of received packet ; ; received packet buffer buf db 1514d dup(?) ;received packet buffer ; mcbuf db mcmax dup(?) ;multicast address list buf ; code ends end start