File Archive

 
Output of file : UBNICPC.ASM contained in archive : PKTD11B.ZIP

version equ 1

;History:525,47 0

; Copyright, 1990-1992, Russell Nelson, Crynwr Software

; 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 bad_command_intercept
bad_command_intercept:
;called with ah=command, unknown to the skeleton.
;exit with nc if okay, cy, dh=error if not.
mov dh,BAD_COMMAND
stc
ret

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 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:code
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:
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


public timer_isr
timer_isr:
;if the first instruction is an iret, then the timer is not hooked
iret

;any code after this will not be kept. Buffers used by the program, if any,
;are allocated from the memory between end_resident and end_free_mem.
public end_resident,end_free_mem
end_resident label byte
end_free_mem label byte


public usage_msg
usage_msg db "usage: ubnic [options] ",CR,LF,'$'

public copyright_msg
copyright_msg db "Packet driver for the UB PC/NIC, version ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,CR,LF
db '$'

int_no_name db "Interrupt number ",'$'
base_addr_name db "Memory 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

;-> the assigned Ethernet address of the card.
extrn rom_address: byte

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

movseg ds,es
mov di,offset rom_address
push ds
mov ds,base_addr
mov si,offset eaddr_rom
repmov EADDR_LEN
pop ds

mov si,offset rom_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