Category : Network Files
Archive   : TCP_SRC.ZIP
Filename : UBNICPC.ASM
;History:25,1 0
; Copyright, 1990, Russell Nelson
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, version 1.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
include defs.asm
;some generic information about buffer lengths, etc
XMITBUF_LEN equ 800h ;Transmit buffer length
RPAGE_LEN equ 128 ;receive page is 128 bytes
NUM_RPAGES equ 96 ;There are 96 receive pages
NUM_XBUFS equ 2 ;Number of xmit buffers
NUM_XBUFS_MASK equ 1
ub_seg segment at 0
org 10h
eaddr_rom db 6 dup(?) ;Has serial # in 3 & 4th bytes
org 2080h
tsa_msb db ? ;transmitter start address, high order 4
;bits on write, start send on read
tsa_lsb db ? ;Transmitter start address, least
;significant byte on write, clear PAV
;on read
;Control / status register (CSR)
csr db ? ;Control on write, status on read port
;Command bits
TxRdyIntEn equ 080h ;allow TxRdy interrupts
PAVIntEn equ 040h ;Allow PAV to gen. intr.
SFTInt equ 020h ;generate an interrupt reg.
TimIntEn equ 010h ;allow Time interrupt req.
;status bits
NTxRdyInt equ 080h ;Not req. TxRdy inter.
NPAVInt equ 040h ;Not req. Pav intr.
NSFTInt equ 020h ;Not req. SFTInt inter.
NTimInt equ 010h ;Not req. timer inter.
NRINT equ 008h ;Not req. recv inter.
NTINT equ 004h ;Not req. xmit inter.
TPOK equ 002h ;Packet okay of some type
TXDONE equ 001h ;Transmit done
;Page pointer register definitions
ffp label byte ;ffp - first free page register
epp label byte ;epp - empty page pointer register
pp db ? ;ffp on write/epp on read
;ffp bits
NIC_IE equ 080h ;NIC interrupts enable
;epp bits
PAV equ 080h ;Page available
RPAGE_BITS equ 07fh
org 2100h
page_ram db NUM_RPAGES dup(?);Receiver buffer pages
org 2180h
tstatus db ? ;Transmitter status 80
;Transmit status bits - EDLC
TX_READY equ 080h ;
TX_BUSY equ 040h ;Carrier detect
TX_TPR equ 020h ;Self received packet sent
TX_SHORTED equ 010h ;Carrier lost
TX_UF equ 008h ;Underflow
TX_COL equ 004h ;Collision
TX_C16 equ 002h ;16 collisions
TX_PE equ 001h ;Parity error
TXERRMASK equ TX_PE or TX_C16 or TX_COL or TX_UF or TX_SHORTED or TX_BUSY
tmask db ? ;Transmitter mask 81
rstatus db ? ;Receiver status
rmask db ? ;Receiver mask 83
tmode db ? ;Transmitter mode 84
rmode db ? ;Receiver mode 85
reset db ? ;Reset line - high on bit 7, 86
tdrlsb db ? ;TDR lsb 87
eaddr_ram db 6 dup(?) ;current Ethernet address.
db ? ;Reserved 8e
tdrmsb db ? ;TDR msb 8f
org 4000h
receive_buf label byte
org 7000h
transmit_buf_1 label byte
org 7800h
transmit_buf_2 label byte
ub_seg ends
code segment word public
assume cs:code, ds:code
public int_no
int_no db 3,0,0,0 ;must be four bytes long for get_number.
base_addr dw 0d000h,0
public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db BLUEBOOK, IEEE8023, 0 ;from the packet spec
driver_type db 8 ;from the packet spec
driver_name db 'PC-NIC',0 ;name of the driver.
driver_function db 2
parameter_list label byte
db 1 ;major rev of packet driver
db 9 ;minor rev of packet driver
db 14 ;length of parameter list
db EADDR_LEN ;length of MAC-layer address
dw GIANT ;MTU, including MAC headers
dw MAX_MULTICAST * EADDR_LEN ;buffer size of multicast addrs
dw 0 ;(# of back-to-back MTU rcvs) - 1
dw 0 ;(# of successive xmits) - 1
int_num dw 0 ;Interrupt # to hook for post-EOI
;processing, 0 == none,
public rcv_modes
rcv_modes dw 7 ;number of receive modes in our table.
dw 0 ;There is no mode zero
dw rcv_mode_1
dw 0 ;don't want to bother.
dw rcv_mode_3
dw 0 ;haven't set up perfect filtering yet.
dw rcv_mode_5
dw rcv_mode_6
current_page db 0 ;current receiver page
active_buffer dw transmit_buf_1
inactive_buffer dw transmit_buf_2
include movemem.asm
public as_send_pkt
; The Asynchronous Transmit Packet routine.
; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
; interrupts possibly enabled.
; Exit with nc if ok, or else cy if error, dh set to error number.
; es:di and interrupt enable flag preserved on exit.
as_send_pkt:
ret
public drop_pkt
; Drop a packet from the queue.
; Enter with es:di -> iocb.
drop_pkt:
assume ds:nothing
ret
public xmit
; Process a transmit interrupt with the least possible latency to achieve
; back-to-back packet transmissions.
; May only use ax and dx.
xmit:
assume ds:nothing
ret
public send_pkt
send_pkt:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
; (only if the high-performance bit is set in driver_function)
;enter with ds:si -> packet, cx = packet length.
;if we're a high-performance driver, es:di -> upcall.
;exit with nc if ok, or else cy if error, dh set to error number.
assume ds:nothing
mov es,base_addr
assume es:ub_seg
cmp cx,GIANT ; Is this packet too large?
ja send_pkt_toobig
cmp cx,RUNT ; minimum length for Ether
jnb oklen
mov cx,RUNT ; make sure size at least RUNT
oklen:
mov di,XMITBUF_LEN ;compute where we're going to
sub di,cx ; be putting the packet.
;games above ensure that the last byte of data to go out is at the
;end of the ub xmit buffer, pos is really the offset from the
;start of that buffer
mov ax,active_buffer ;swap the buffer pointers.
xchg ax,inactive_buffer
mov active_buffer,ax
add di,ax
push di
call movemem
pop di
;Wait for previous packet to finish first
;could do this with interrupts, etc, but with only 2 xmit buffers
;doesn't seem worth the effort
send_pkt_1:
mov al,csr
and al,TPOK or TXDONE
cmp al,TPOK or TXDONE
jne send_pkt_1
mov al,tstatus ;get xmiter status ...
mov tstatus,0fh ;... and clear status bits
test al,TXERRMASK ;any errors?
je send_pkt_2
call count_out_err ;yes, count them.
send_pkt_2:
;all done above, we can play with TSA now!
;give offset
mov ax,di
mov tsa_lsb,al
mov tsa_msb,ah
cmp al,tsa_msb ;Start the send in action (SIDEEFFECT)
clc
ret
send_pkt_toobig:
mov dh,NO_SPACE
stc
ret
public get_address
get_address:
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
assume ds:code
cmp cx,EADDR_LEN ;make sure that we have enough room.
jb get_address_2
push ds
mov ds,base_addr
mov si,offset eaddr_rom
mov cx,EADDR_LEN
rep movsb
pop ds
mov cx,EADDR_LEN
clc
ret
get_address_2:
stc
ret
public set_address
set_address:
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
assume ds:nothing
cmp cx,EADDR_LEN ;ensure that their address is okay.
je set_address_4
mov dh,BAD_ADDRESS
stc
jmp short set_address_done
set_address_4:
mov es,base_addr
mov di,offset eaddr_ram
rep movsb
; call initialize ;initialize with our new address.
set_address_okay:
mov cx,EADDR_LEN ;return their address length.
clc
set_address_done:
push cs
pop ds
assume ds:code
ret
; 0 - accept no packets
rcv_mode_1:
mov es,base_addr
assume es:ub_seg
mov rmode,0
ret
rcv_mode_3:
; 1 - accept node id packets, multicasts which match first three
; bytes of node id, and broadcasts
mov es,base_addr
assume es:ub_seg
mov rmode,1
ret
; 2 - accept node id packets, all multicasts and broadcasts
rcv_mode_5:
mov es,base_addr
assume es:ub_seg
mov rmode,2
ret
; 3 - take all, promiscous
rcv_mode_6:
mov es,base_addr
assume es:ub_seg
mov rmode,3
ret
public set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, cx = number of addresses.
;return nc if we set all of them, or cy,dh=error if we didn't.
mov dh,NO_MULTICAST
stc
ret
public terminate
terminate:
mov es,base_addr
assume es:ub_seg
mov ffp,0 ;set ffp to zero - disabling PAV
mov csr,0 ;Turn off interrupt masks here and ...
mov tmask,0 ;reset edlc interrupt masks
mov rmask,0
mov reset,80h ;Turn on reset line
ret
public reset_interface
reset_interface:
;reset the interface.
assume ds:code
ret
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type, dl = packet class.
extrn recv_find: near
;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
extrn recv_copy: near
extrn count_in_err: near
extrn count_out_err: near
public recv
recv:
;called from the recv isr. All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
assume ds:code
mov es,base_addr
assume es:ub_seg
recv_3:
test epp,PAV
jne recv_4
jmp recv_2 ;yes.
recv_4:
;checking against epp is a pain actually...
xor cx,cx ;start with zero length
mov bl,current_page ;get the first page.
xor bh,bh
mov dx,bx ;remember the first page.
count_pages_1:
mov al,page_ram[bx] ;Get page descriptor
and al,RPAGE_BITS ;Only page offset bits PLEASE
inc al ;Add 1 to offset to make length
xor ah,ah
add cx,ax ;Add in current page length
mov al,page_ram[bx] ;Get page descriptor
inc bx ;go to the next page
cmp bx,NUM_RPAGES ;wrap around if necessary.
jb count_pages_2
xor bx,bx
count_pages_2:
test al,80h ;End page?
je count_pages_1 ;yes, go add this one's length.
mov current_page,bl
mov ax,dx ;multiply dx by RPAGE_LEN.
xchg ah,al
ror ax,1
mov di,ax
add di,offset receive_buf ;get the starting address.
add di,EADDR_LEN+EADDR_LEN ;skip the ethernet addreses and
; point to the packet type.
push bx
push cx
push dx
mov dl, BLUEBOOK ;assume bluebook Ethernet.
mov ax, es:[di]
xchg ah, al
cmp ax, 1500
ja BlueBookPacket
inc di ;set di to 802.2 header
inc di
mov dl, IEEE8023
BlueBookPacket:
call recv_find ;look up our type.
assume es:nothing
pop dx
pop cx
pop bx
mov ax,es ;is this pointer null?
or ax,di
je just_discard ;yes - just free the frame.
push cx
push di
push es
cmp dx,bx ;did we wrap around (first >last?)
jbe no_wrap ;no, just do one move.
;we wrapped around the buffer, so we have to move in two halves.
;move the first part.
mov ax,dx ;multiply dx by RPAGE_LEN.
xchg ah,al
ror ax,1
mov si,ax
add si,offset receive_buf ;get the starting address.
mov ax,NUM_RPAGES ;Compute the number of pages to move.
sub ax,dx
xchg ah,al ;convert to pages (*RPAGE_LEN).
ror ax,1
cmp cx,ax ;are we moving less than that?
jbe no_wrap ;yes, just move it.
sub cx,ax ;reduce the total count by this count.
push cx ;preserve the second part's count
mov cx,ax ;get the first part's count.
push ds
mov ds,base_addr
assume ds:nothing
call movemem
assume ds:code
pop ds
pop cx ;get back the count
xor dx,dx
no_wrap:
mov ax,dx ;multiply dx by 128.
xchg ah,al
ror ax,1
mov si,ax
add si,offset receive_buf
mov ds,base_addr
assume ds:nothing
call movemem
assume ds:code
pop ds
pop si
pop cx
call recv_copy
push cs
pop ds
just_discard:
mov es,base_addr
assume es:ub_seg
mov al,epp ;Any more packets left?
and al,RPAGE_BITS
cmp al,current_page
je recv_5
jmp recv_3
recv_5:
cmp al,tsa_lsb ;Clearing pav bit (SIDEEFFECT)
recv_2:
;Ack the interrupt by turning bit on and off
mov al,current_page ;Set first free page pointer
mov ffp,al
or al,NIC_IE ;set interrupt bit, was off
mov ffp,al
recv_1:
ret
public recv_exiting
recv_exiting:
;called from the recv isr after interrupts have been acknowledged.
;Only ds and ax have been saved.
assume ds:nothing
ret
;any code after this will not be kept after initialization.
end_resident label byte
public usage_msg
usage_msg db "usage: ubnic [-n] [-d] [-w]
public copyright_msg
copyright_msg db "Packet driver for the UB PC/NIC, version ",'0'+majver,".",'0'+version,CR,LF
db '$'
int_no_name db "Interrupt number ",'$'
base_addr_name db "Memory address ",'$'
our_address db 6 dup(?) ;temporarily hold our address
extrn set_recv_isr: near
;enter with si -> argument string, di -> wword to store.
;if there is no number, don't change the number.
extrn get_number: near
;enter with si -> argument string, di -> wword to print.
extrn print_number: near
public parse_args
parse_args:
;exit with nc if all went well, cy otherwise.
mov di,offset int_no
call get_number
mov di,offset base_addr
call get_number
clc
ret
public etopen
etopen:
cli
cmp base_addr,-1 ;did they ask for auto-configure?
jne no_auto_config
mov bx,8000h ;yes, they *could* address it there.
auto_config_1:
mov es,bx
assume es:ub_seg
cmp word ptr es:eaddr_rom[0],00h + 0ddh * 256
jne auto_config_2
cmp byte ptr es:eaddr_rom[2],01h
je auto_config_3
auto_config_2:
add bx,800h ;move up by 32K
jnc auto_config_1 ;go if we didn't overflow.
sti
stc ;say that we couldn't init.
ret
auto_config_3:
mov base_addr,bx ;remember where we found it.
mov base_addr+2,0 ;and nuke the rest of the -1.
no_auto_config:
mov es,base_addr
assume es:ub_seg
mov reset,80h ;Turn on reset line to tie down NIC
mov rmode,0 ;no packets for now
mov tmode,0 ;Turn on loopback
mov al,epp ;start ffp at epp - eliminates bogus ....
and al,07fh
mov current_page,al ;... packets if we've been started before
mov ffp,al ;Make sure interrupt enable is dead
cmp al,tsa_lsb ;Clearing pav bit (SIDEEFFECT)
mov tstatus, 0fh ;Clear xmit status bits
mov tmask,0 ;No xmit intrs
mov rmask,0 ;'Packet okay' only?
mov rstatus,0ffh ;Clear receiver status bits
push ds
pop es
mov di,offset our_address
mov cx,EADDR_LEN
call get_address
mov si,offset our_address
mov cx,EADDR_LEN
call set_address
;Since first packet will get sent no matter what we do, let's make it
;something everyone (include me) will ignore
;set length to zero
mov tsa_lsb,0ffh ;Get last 8 bits
mov tsa_msb,0fh ;and top 4 bits
mov reset,0 ;Clear reset - expect packet to go
;Wait for packet to go!
etopen_1:
mov al,csr
and al,TPOK or TXDONE
cmp al,TPOK or TXDONE
jne etopen_1
mov tstatus,0fh ;clear xmiter status
mov tmode,0ah ;Clear loopbk, set parity enable
mov csr,0 ;make sure this interrupt was off first
mov csr,PAVIntEn ;Page frame available interrupt only
mov al,current_page ;Set first free page register ...
or al,NIC_IE
mov ffp,al ;... and make sure interrupts enabled
mov al,epp ;get epp again
test al,PAV
je etopen_2
cmp al,tsa_lsb ;Try clearing pav bit (SIDEEFFECT)
etopen_2:
;enable receiving now
; 1 - accept node id packets, multicasts which match first three
; bytes of node id, and broadcasts
mov rmode,1
;
; Now hook in our interrupt
;
call set_recv_isr
sti
mov al, int_no ; Get board's interrupt vector
add al, 8
cmp al, 8+8 ; Is it a slave 8259 interrupt?
jb set_int_num ; No.
add al, 70h - 8 - 8 ; Map it to the real interrupt.
set_int_num:
xor ah, ah ; Clear high byte
mov int_num, ax ; Set parameter_list int num.
;if all is okay,
mov dx,offset end_resident
clc
ret
;if we got an error,
stc
ret
public print_parameters
print_parameters:
;echo our command-line parameters
mov di,offset int_no
mov dx,offset int_no_name
call print_number
mov di,offset base_addr
mov dx,offset base_addr_name
call print_number
ret
code ends
end
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/