Category : Network Files
Archive   : PKTD11A.ZIP
Filename : EN301.ASM

 
Output of file : EN301.ASM contained in archive : PKTD11A.ZIP
version equ 2
;******************************************************************************;
;* File: EN301.ASM
;* Packet Driver for Multi-Tech Systems EN301xx series
;* ETHERNET adapters.
;* Auth: Chris Elmquist N0JCF
;* Date: April 1, 1991
;* Removed Microchannel support and added code to
;* support serial EEPROM on Multi-Tech card.
;*
;* Copyright (C) 1991 Chris Elmquist
;*
;* 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; either version 1, or (at your option)
;* any later version.
;*
;* 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.
;*
;******************************************************************************;
;* Original work from: *;
;* File: TiaraFTP.ASM *;
;* Auth: Brian Fisher *;
;* Queens University *;
;* Computing and Communications Services *;
;* Rm 2-50 Dupuis Hall *;
;* Kingston Ontario *;
;* Date: January 24 1990 *;
;*============================================================================*;
;* Revs: January 25 1990 V 1.6.0 Clean up code and document. *;
;* Feb 24 1991 V 1.8.2 Gets IRQ and I/O info from MCH POS regs*;
;* *;
;*============================================================================*;
;* *;
;* Thanks, Mehdi Safipour, of Tiara Computer Systems, who supplied the *;
;* programming manual and examples. *;
;* *;
;*============================================================================*;
;* *;
;* Logic - *;
;* *;
;* Initialization - classic, by-the-book initialization, with one *;
;* exception: The manual didn't mention the fact that receive *;
;* interrupts will not always work unless the receive buffer is *;
;* vacuumed. *;
;* *;
;* Byte/Word I/O mode was determined by code ruthlessly copied from *;
;* NI5010.ASM, auth: Russ Nelson. *;
;* *;
;* Transmit-no surprises, data goes whoosh! *;
;* *;
;* Receive-interrupt driven receive makes upcalls to inform the ULP of *;
;* its status. The 14 byte ethernet header is copied from the card to *;
;* a temporary buffer, to determine the ether-type. Recv_find is called *;
;* to acquire a buffer from the ULP. If no buffer, the packet is dropped.*;
;* If a buffer is acquired, the packet is copied into it, and recv_copy *;
;* informs the ULP that the data is there. *;
;* *;
;******************************************************************************;
;

DLCR_XMIT_STAT EQU 0 ; EtherStar I/O Register offsets
DLCR_XMIT_MASK EQU 1
DLCR_RECV_STAT EQU 2
DLCR_RECV_MASK EQU 3
DLCR_XMIT_MODE EQU 4
DLCR_RECV_MODE EQU 5
DLCR_ENABLE EQU 6
DLCR_TDR_LOW EQU 7
DLCR_NODE_ID EQU 8
DLCR_TDR_HIGH EQU 0FH
BMPR_MEM_PORT EQU 10H
BMPR_PKT_LEN EQU 12H
BMPR_DMA_ENABLE EQU 14H
EECS_PORT EQU 18H
EECLK_PORT EQU 19H
EEDI_PORT EQU 1AH
EEDO_PORT EQU 1AH
TMST EQU 80h
TMT_OK EQU 80h
BUF_EMPTY EQU 40h
card_disable equ 80h ; written to DLCR_ENABLE to disable card
card_enable equ 0h ; written to DLCR_ENABLE to enable card
clear_status equ 00001111B ; used to clear status info
;
; !!!!!!!!--------
; !!!!!!!+--------CLEAR BUS WRITE ERROR
; !!!!!!+---------CLEAR 16 COLLISION
; !!!!!+----------CLEAR COLLISION
; !!!!+-----------CLEAR UNDERFLOW
; !!!+------------NC
; !!+-------------NC
; !+--------------NC
; +---------------NC
;
no_tx_irqs equ 0 ; written to clear transmit IRQs
clr_rcv_status equ 0CFh ; clears receive status
en_rcv_irqs equ 10000000B ; enable receive interrupts
;
; !!!!!!!!--------
; !!!!!!!+--------ENABLE OVERFLOW
; !!!!!!+---------ENABLE CRC
; !!!!!+----------ENABLE ALIGN
; !!!!+-----------ENABLE SHORT PKT
; !!!+------------DISABLE REMOTE RESET
; !!+-------------RESERVED
; !+--------------RESERVED
; +---------------ENABLE PKT READY
;
xmit_mode equ 00000010B
; !!!!!!!!---------ENABLE CARRIER DETECT
; !!!!!!!+---------DISABLE LOOPBACK
;
;
recv_mode equ 00000010B ; set receive mode
;
; !!!!!!!!---------ACCEPT ALL PACKETS
; !!!!!!!+---------ACCEPT PHYSICAL, MULTICAST, AND
; !!!!!!+----------BROADCAST PACKETS
; !!!!!+-----------DISABLE REMOTE RESET
; !!!!+------------DISABLE SHORT PACKETS
; !!!+-------------USE 6 BYTE ADDRESS
; !!+--------------NC
; !+---------------NC
; +----------------DISABLE CRC TEST MODE

debug = 0

include defs.asm

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
io_adr dw 300h,0 ; default I/O address
extrn is_186: byte ;=0 if 808[68], =1 if 80[123]86.

public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db BLUEBOOK,IEEE8023,0 ;from the packet spec
driver_type db 1 ;from the packet spec
driver_name db 'EN301FTP',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
dw rcv_mode_3
dw 0 ;haven't set up perfect filtering yet.
dw 0
dw rcv_mode_6


;
; Receive Packet Header Buffer: Required because addresses and e-type
; must be read from Tiara card before upcall to find a buffer can be
; made. Need the number of bytes, and the e-type for the call...
;
ether_buff db EADDR_LEN dup(?)
db EADDR_LEN dup(?)
ether_type db 4 dup(?)
usr_ptr dd ? ; temp storage or recv_buff ptr

writebport macro from_base,value
mov dx,cs:[io_adr] ; write byte value to port
add dx,from_base
mov al,value
out dx,al
endm

readbport macro from_base
mov dx,cs:[io_adr]
add dx,from_base
in al,dx
endm

port macro from_base
mov dx,cs:[io_adr]
add dx,from_base
endm

mark = 0F90h ; marker debug pos on screen 25

marker macro st,nd
IF debug NE 0 ; do marker if debug <> 0
pushf ; show 2 char marker on
push es ; 25th line, 1st column
push ax
mov ax,0B800h
mov es,ax
mov al,'&st&'
mov byte ptr es:[mark],al
mov al,byte ptr es:[mark+1] ; get color value
inc al
and al,0Fh
or al,1
mov byte ptr es:[mark+1],al ; advance it to show activity
mov al,'&nd'
mov byte ptr es:[mark+2],al
mov al,byte ptr es:[mark+3]
inc al
and al,0Fh
or al,1
mov byte ptr es:[mark+3],al
pop ax
pop es
popf
ENDIF
endm

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 ds:si -> packet, cx = packet length.
;exit with nc if ok, pr else cy if error, dh set to error number.

assume ds:nothing
marker T,X
inc cx
and cx,not 1 ; round to nearest even number
cmp cx,RUNT ; big enough?
jae send_runt_ok
mov cx,RUNT ; at least runt!
send_runt_ok:
cmp cx,GIANT ; small enough?
jbe send_size_ok
mov dh,NO_SPACE
stc ; Error
ret
send_size_ok:
push cx
shr cx,1 ; words to send
;
; 8086/8088 byte mode send
;
mov dx,cs:[io_adr]
add dx,BMPR_MEM_PORT
cmp cs:is_186,0 ; use BYTE or WORD mode?
jne send_w_mode
xb:
lodsw ; load word, ind ds:si
out dx,al
xchg ah,al
out dx,al
loop xb ; set packet length (byte mode)
pop ax
or ah,TMST
mov dx,cs:[io_adr]
add dx,BMPR_PKT_LEN
out dx,al ; write BMPR2, then BMPR3 to
xchg al,ah ; initiate byte mode transmit
inc dx
out dx,al
jmp wait_tmt_ok
send_w_mode:
.286
rep outsw
.8086
pop ax
or ah,TMST
mov dx,cs:[io_adr]
add dx,BMPR_PKT_LEN
out dx,ax
wait_tmt_ok:
xor cx,cx
mov dx,cs:[io_adr]
IF DLCR_XMIT_STAT NE 0
add dx,DLCR_XMIT_STAT
ENDIF
wait_tmt:
in al,dx ; read status register until timeout...
test al,TMT_OK
jnz send_ok
loop wait_tmt
stc
ret
send_ok:
clc
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
ret


rcv_mode_1:
writebport DLCR_RECV_MODE,0 ; don't receive any packets
ret


rcv_mode_3:
writebport DLCR_RECV_MODE,2 ; receive mine, broads, and multis.
ret


rcv_mode_6:
writebport DLCR_RECV_MODE,3 ; receive all packets.
ret


public set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, ax = number of addresses,
; cx = number of bytes.
;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:
writebport DLCR_RECV_MODE,0 ; don't receive any packets
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've 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
marker R,X

;clear receive masks to prevent further IRQs

writebport DLCR_RECV_MASK,0
recv_0:
mov ax,cs
mov ds,ax
writebport DLCR_RECV_STAT,clr_rcv_status

;are there any packets to read?

mov dx,cs:[io_adr]
add dx,DLCR_RECV_MODE
in al,dx
test al,BUF_EMPTY
jz recv_1 ; 0 if at least one valid pkt..
jmp recv_99

;yes, read out a receive packet...

recv_1:
cmp cs:is_186,0
jne recv11
jmp read_b_mode
;
; read packet out in word mode
;
recv11:
mov dx,cs:[io_adr] ; get status and reserved byte
add dx,BMPR_MEM_PORT
in ax,dx
in ax,dx ; get packet size
inc ax ; convert to words
and ax,not 1 ; save it for copy out...
push ax
;read first 14 bytes from receive buffer into ether_buff
mov ax,cs
mov es, ax
mov di,offset ether_buff
mov cx,16/2 ; word mode, remember...
.286
rep insw ; read words into ether_buff
.8086

;
; If the sender is myself, ignore the packet. This allows async
; send/receive without messing up...
;
mov si,offset ether_buff+EADDR_LEN ; we want the SOURCE
mov di,offset my_address
mov cx,EADDR_LEN/2
repe cmpsw
jne not_mine
jmp word_flush ; mine, so flush it
;
; cx = length, es:di = pointer to ethertype
;
not_mine:
pop cx
push cx
mov di,offset ether_type
mov ax,cs
mov es,ax ; es:di -> ether type, cx = size# bytes
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 ; got a buffer?
mov ax,es
or ax,di ; pointer zero?
je word_flush ; no pointer, discard data
;
; es:di -> users buffer, do copy...
; ds:si -> source of copy
;
mov cs:[usr_ptr.segm],es; save ULP pointer
mov cs:[usr_ptr.offs],di
mov ax,cs
mov ds,ax
mov si,offset ether_buff ; copy header to users buffer
mov cx,16/2 ; 8 words to copy
rep movsw
mov dx,cs:[io_adr] ;copy rest of data to user
add dx,BMPR_MEM_PORT ; buffer in es:di ->
pop cx
push cx
sub cx,16
shr cx,1
.286
rep insw ; read word, store at es:di ->
.8086
pop cx ;call recv_copy to say copy done
lds si,cs:[usr_ptr]
call recv_copy
jmp recv_0 ; go get another packet
word_flush:
mov dx,cs:[io_adr]
add dx,BMPR_MEM_PORT
pop cx
sub cx,16 ; adjust word count
shr cx,1
word_f:
in ax,dx
loop word_f
jmp recv_0
;
; go see of any more packets comming....
;
; READ in BYTE MODE
;
read_b_mode:
mov dx,cs:[io_adr] ;get status and reserved byte
add dx,BMPR_MEM_PORT
in al,dx ; vacuum status and reserved
in al,dx
in al,dx ; get packet size
xchg al,ah ; low byte in ah
in al,dx ; get packet size
xchg al,ah ; fix size ah/al order
push ax ; keep number of bytes
; read first 16 bytes from receive buffer into ether_buff
mov ax,cs
mov es,ax
mov di,offset ether_buff
mov cx,16 ; byte mode, 16 byte header
rdb:
in al,dx ; read 16 bytes into ether_buff
stosb
loop rdb
;
; If the sender is myself, ignore the packet.
;
mov si,offset ether_buff+EADDR_LEN ; we want the SOURCE
mov di,offset my_address
mov cx,EADDR_LEN/2
repe cmpsw
jne not_mineb
jmp byte_flush ; mine, so flush it
;
; cx = length, es:di = pointer to ethertype
;
not_mineb:
pop cx
push cx
mov di,offset ether_type
mov ax,cs
mov es,ax ; es:di -> ether type, cx = size#bytes
mov dl, BLUEBOOK ;assume bluebook Ethernet.
mov ax, es:[di]
xchg ah, al
cmp ax, 1500
ja BlueBookPacket1
inc di ;set di to 802.2 header
inc di
mov dl, IEEE8023
BlueBookPacket1:
call recv_find ; got a buffer?
mov ax,es
or ax,di ; pointer zero?
je byte_flush ; no pointer, discard data
;
; es:di -> users buffer, do copy...
; ds:si -> source of copy

;
mov cs:[usr_ptr.segm],es ; save ULP pointer
mov cs:[usr_ptr.offs],di
mov ax,cs
mov ds,ax
mov si,offset ether_buff ; copy header to users buffer
mov cx,16/2 ; 16 bytes in header to copy
rep movsw
mov dx,cs:io_adr ; copy rest of data to users
add dx,BMPR_MEM_PORT ; buffer in es:di ->
pop cx
push cx
sub cx,14
cpyb:
in al,dx ; read byte
stosb ; store at es:di ->
loop cpyb
pop cx ; call recv_copy to say copy done
lds si,cs:[usr_ptr]
call recv_copy
jmp recv_0 ; go get another packet...
byte_flush:
mov dx,cs:io_adr
add dx,BMPR_MEM_PORT
pop cx
sub cx,16 ; adjust byte count header
byte_f:
in al,dx
loop byte_f
jmp recv_0 ; go to see if any more packets comming...
recv_99:
; receive ok, restore recive mask and exit
;
writebport DLCR_RECV_MASK,en_rcv_irqs
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

io_adr_msg db "I/O Base Address: ",'$'
int_no_msg db " Interrupt Level: ",'$'

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

public copyright_msg
copyright_msg label byte
db CR,LF
db "EN301: FTP Driver for Multi-Tech EN301, Version ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version, CR,LF
db " - for PC and PC-AT",CR,LF
db "portions - Copyright 1991 by Chris Elmquist and",CR,LF
db " Copyright 1990, 1991 Queens University by Brian Fisher",CR,LF
db CR,LF,'$'

extrn set_recv_isr: near
;enter with si-> argument string,di->wword to store.
;if there is no number, don't change the number.

;enter with si -> argument string, di -> word to store.
;if there is no number, don't change the number.
extrn get_number: near

;enter with dx -> name of word, di -> dword to print.
extrn print_number: near

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

;-> the current Ethernet address of the card.
extrn my_address: byte

public parse_args
parse_args:

; parse I/O base address and hardware interrupt number from the
; command line.
;
mov bx,offset int_no_msg ; first comes the interrupt level
mov di,offset int_no
call get_number
jc _parse_exit
mov bx,offset io_adr_msg ; then the I/O base address
mov di,offset io_adr
call get_number
_parse_exit:
clc
ret

; Clock the LSB of AX into the EEPROM (DI is inverted by the hardware)
clkbit:
push ax
port EEDI_PORT
not ax ; invert data because of hardware
out dx,al
mov al,1 ; clock high
port EECLK_PORT
out dx,al
mov al,0 ; clock low
out dx,al
pop ax
ret

public etopen
etopen:
assume ds:code
;
; Set Node ID:
;
writebport EECS_PORT,1 ; CS high on 9346
mov al,0
call clkbit

; clock the 6 byte (3 words) Ethernet address out of the EEPROM
mov cx,3
mov bx,0
eegetadr1:
mov al,1 ; READ opcode is '110'
call clkbit
call clkbit
mov al,0
call clkbit
mov ax,bx
shr ax,1

; send the address in AX to EEPROM
push cx
mov cx,5
ror ax,cl ; move A5 to LSB
call clkbit
saddr1: rol ax,1 ; A4 to LSB, then A3, etc...
call clkbit ; LSB of AX gets clocked in
loop saddr1

; read the data from the currently addressed EEPROM cell
push bx
mov cx,16 ; set counter to read 16 bits of data
mov bx,0
rdd: shl bx,1
call clkbit
readbport EEDO_PORT
and ax,1
or bx,ax
loop rdd
mov al,bh
mov ah,bl
pop bx
pop cx

mov word ptr rom_address[bx],ax
inc bx
inc bx

; perform a standby operation after each read
writebport EECS_PORT,0
call clkbit
call clkbit
writebport EECS_PORT,1
loop eegetadr1

writebport EECS_PORT,0 ; deselect the EEPROM and de-reset
; the Fujitsu

writebport DLCR_ENABLE,card_disable ; disable etherstar
writebport DLCR_XMIT_STAT,clear_status ; clr xmit status
writebport DLCR_XMIT_MASK,no_tx_irqs ; disable xmit IRQ's
writebport DLCR_RECV_STAT,clr_rcv_status ; clear rcv status
writebport DLCR_RECV_MASK,en_rcv_irqs ; enable rcv IRQ's
writebport DLCR_XMIT_MODE,xmit_mode ; set xmit mode
writebport DLCR_RECV_MODE,recv_mode ; set receive mode

mov si,offset rom_address ; write the node address
mov cx,6 ; into the 86950 registers
port DLCR_NODE_ID
etopen1:
lodsb
out dx,al
inc dx
loop etopen1

; repeat until BUF_EMPTY vacuum data port
mov dx,cs:io_adr
mov bx,dx
add bx,DLCR_RECV_MODE
add dx,BMPR_MEM_PORT
cmp cs:is_186,0
je vac_88
vac_86:
in ax,dx
xchg dx,bx
in ax,dx
xchg dx,bx
test al,BUF_EMPTY
jz vac_86
jmp short all_done
vac_88:
in al,dx
xchg dx,bx
in al,dx
xchg dx,bx
test al,BUF_EMPTY

jz vac_88
all_done:
writebport DLCR_RECV_STAT,clr_rcv_status; clear possible underflow
writebport DLCR_ENABLE,card_enable
call set_recv_isr ; install receive IRQ routine

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.

clc ; if all is okay,
ret

public print_parameters
print_parameters:
;echo our command-line parameters
mov di,offset int_no ; interrupt level.
mov dx,offset int_no_msg
call print_number
mov di,offset io_adr ; now comes the I/O base address
mov dx,offset io_adr_msg
call print_number
ret

code ends

end


  3 Responses to “Category : Network Files
Archive   : PKTD11A.ZIP
Filename : EN301.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. 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/