home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
archives
/
packetdrivers.tar.gz
/
pd.tar
/
src
/
tcenet.asm
< prev
next >
Wrap
Assembly Source File
|
1995-06-25
|
34KB
|
1,423 lines
version equ 0
;History:916,1
; 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.
.286 ;the tcenet requires a 286.
comment \
We have to lock all the data structures accessed by the board. We
also have to keep track of which ones have been locked and which have not.
\
include defs.asm
pause_ macro
; jmp $+2
;
; The reason for the pause_ macro is to establish a minimum time between
; accesses to the card hardware. The assumption is that the fetch and execution
; of the jmp $+2 instruction will provide this time. In a fast cache machine
; this may be a false assumption. In a fast cache machine, there may be
; NO REAL TIME DIFFERENCE between the two I/O instruction streams below:
;
; in al,dx in al,dx
; jmp $+2
; in al,dx in al,dx
;
; To establish a minimum delay, an I/O instruction must be used. A good rule of
; thumb is that ISA I/O instructions take ~1.0 microseconds and MCA I/O
; instructions take ~0.5 microseconds. Reading the NMI Status Register (0x61)
; is a good way to pause on all machines.
;
; The National 8390 Chip (NIC) requires 4 bus clocks between successive
; chip selects (National DP8390 Data Sheet Addendum, June 1990 -- it took them
; long enough to figure this out and tell everyone) or the NIC behaves badly.
; Therefor one I/O instruction should be inserted between each successive
; NIC I/O instruction that could occur 'back - to - back' on a fast cache
; machine.
; - gft - 910529
;
push ax
in ax, 61h
pop ax
;
endm
DMA_8MASK_REG equ 0Ah
DMA_8MODE_REG equ 0Bh
DMA_16MASK_REG equ 0D4h
DMA_16MODE_REG equ 0D6h
CASCADE_MODE equ 0C0h
SET_DMA_MASK equ 4
DMA_CHANNEL_FIELD equ 3
;tcenet registers.
EBASE equ 0
CONFIG equ 0eh
PORTPAGE equ 0fh
SONICREG equ 10h
;we don't use setport to refer to Sonic registers, because of the crufty
;port paging.
sncport macro new_port_no
; if ((new_port_no xor port_no) and (not 0fh)) or (port_no EQ 0)
loadport
setport PORTPAGE
push ax
mov al, (new_port_no shr 1)
out dx,al
in al,61h ;see pause_ macro.
pop ax
; endif
setport <(SONICREG+(new_port_no and 0fh))>
endm
RDA_COUNT equ 8
TRANSMIT_BUF_COUNT equ 1
RECEIVE_BUF_COUNT equ 2
TRANSMIT_BUF_SIZE equ 1520
RECEIVE_BUF_SIZE equ 1522*2
EOL equ 1 ;descriptor EOL flag
;-----------------------------------------------------------------------------
;
; Sonic Register Definitions
; Sonic command register bits
LCAM equ 0200h ;Load CAM
RRRA equ 0100h ;Read RRA
RST equ 0080h ;Software Reset
ST equ 0020h ;Start Timer
STP equ 0010h ;Stop Timer
RXEN equ 0008h ;Receiver Enable
RXDIS equ 0004h ;Receiver Disable
TXP equ 0002h ;Transmit packets
HTX equ 0001h ;Halt transmission
; Sonic Data Configuration Register Bits
EXBUS equ 8000h ;Extended Bus mode
LBR equ 2000h ;Latched Bus Retry
PO1 equ 1000h ;Programmable Outputs
PO0 equ 0800h ;. .
SBUS equ 0400h ;Synchronous Bus Mode
USR1 equ 0200h ;User Definable Pins
USR0 equ 0100h ;. .
WC1 equ 0080h ;Wait State Control
WC0 equ 0040h ;. .
DW32 equ 0020h ;Data Width Select
BMS equ 0010h ;Block Mode Select for DMA
RFT1 equ 0008h ;Receive FIFO Threshold
RFT0 equ 0004h ;. .
TFT1 equ 0002h ;Transmit FIFO Threshold
TFT0 equ 0001h ;. .
; Sonic Receive Control Register bits
ERR equ 8000h ;Accept Packet with Errors
RNT equ 4000h ;Accept Runt Packets
BRD equ 2000h ;Accept Broadcast Packets
PRO equ 1000h ;Accept all Physical Packets (Promiscuous Mode)
AMC equ 0800h ;Accept all Multicast Packets
LB1 equ 0400h ;Loopback Control 1
LB0 equ 0200h ;Loopback Control 0
MCRx equ 0100h ;Multicast Packet Received
BCRx equ 0080h ;Broadcast Packet Received
LPKT equ 0040h ;Last Packet in RBA
CRS equ 0020h ;Carrier Sense Activity
COL equ 0010h ;Collision Activity
CRCR equ 0008h ;CRC Error
FAER equ 0004h ;Frame Alignment Error
LBK equ 0002h ;LoopBack Packet Received
PRX equ 0001h ;Packet Received OK
; Transmit Control Register bits
PINTR equ 8000h ;Programmable Interrupt
POWC equ 4000h ;Program 'Out of Window Collision' Timer
CRCI equ 2000h ;CRC Inhibit
EXDIS equ 1000h ;Disable Excessive Deferral Timer
EXD equ 0400h ;Excessive Deferral
DEF equ 0200h ;Deferred Transmission
NCRS equ 0100h ;No CRS
CRSL equ 0080h ;CRS Lost
EXC equ 0040h ;Excessive Collisions
OWC equ 0020h ;Out of Window Collision
PMB equ 0008h ;Packet Monitored Bad
FU equ 0004h ;Tx FIFO UnderRun
BCM equ 0002h ;Byte Count Mismatch
PTX equ 0001h ;Packet Transmitted OK
TXDONE equ EXD or CRSL or EXC or PTX ;nonzero when transmit done.
; Interrupt Mask Register bits
BREN equ 4000h ;Bus Retry Occurred Enable
HBLEN equ 2000h ;HeartBeat Lost Enable
LCDEN equ 1000h ;Load CAM Done Interrupt Enable
PINTEN equ 0800h ;Programmable Interrupt Enable
PRXEN equ 0400h ;Packet Received Enable
PTXEN equ 0200h ;Packet Transmitted OK Enable
TXEREN equ 0100h ;Transmit Error Enable
TCEN equ 0080h ;Timer Complete Enable
RDEEN equ 0040h ;Receive Descriptors Exhausted Enable
RBEEN equ 0020h ;Receive Buffers Exhausted Enable
RBAEEN equ 0010h ;Receive Buffer Area Exceeded Enable
CRCEN equ 0008h ;CRC Tally Counter Warning Enable
FAEEN equ 0004h ;FAE Tally Counter Warning Enable
MPEN equ 0002h ;MP Tally Counter Warning Enable
RFOEN equ 0001h ;Receive FIFO Overrun Enable
; these are the interrupts we enable:
enabled_IMR equ (PTXEN or TXEREN or PRXEN or RBEEN or BREN)
; Interrupt Status Register bits
BR equ 4000h ;Bus Retry Occurred
HBL equ 2000h ;HeartBeat Lost
LCD equ 1000h ;Load CAM Done
PINT equ 0800h ;Programmable Interrupt
PKTRX equ 0400h ;Packet Received
TXDN equ 0200h ;Transmission Done
TXER equ 0100h ;Transmit Error
TC equ 0080h ;Timer Complete
RDE equ 0040h ;Receive Descriptors Exhausted
RBE equ 0020h ;Receive Buffers Exhausted
RBAE equ 0010h ;Receive Buffer Area Exceeded
CRC equ 0008h ;CRC Tally Counter Rollover
FAE equ 0004h ;FAE Tally Counter Rollover
MP equ 0002h ;Missed Packet Tally Counter Rollover
RFO equ 0001h ;Receive FIFO Overrun
;SONIC registers. The registers are sixteen-bit registers that are also
;byte-addressable. So, the word addresses must be multiplied by two.
; Command and Status Register Offsets
SonicCR equ (0 * 2)
SonicDCR equ (1 * 2)
SonicRCR equ (2 * 2)
SonicTCR equ (3 * 2)
SonicIMR equ (4 * 2)
SonicISR equ (5 * 2)
; Transmit Registers
SonicUTDA equ (6 * 2)
SonicCTDA equ (7 * 2)
; Receive Registers
SonicURDA equ (0dh * 2)
SonicCRDA equ (0eh * 2)
SonicEOBC equ (13h * 2)
SonicURRA equ (14h * 2)
SonicRSA equ (15h * 2)
SonicREA equ (16h * 2)
SonicRRP equ (17h * 2)
SonicRWP equ (18h * 2)
SonicRSC equ (2bh * 2)
; CAM Registers
SonicCEP equ (21h * 2)
SonicCAP2 equ (22h * 2)
SonicCAP1 equ (23h * 2)
SonicCAP0 equ (24h * 2)
SonicCE equ (25h * 2)
SonicCDP equ (26h * 2)
SonicCDC equ (27h * 2)
; Tally Counters
SonicCRCT equ (2ch * 2)
SonicFAET equ (2dh * 2)
SonicMPT equ (2eh * 2)
; Watchdog Counters
SonicWT0 equ (29h * 2)
SonicWT1 equ (2ah * 2)
; Silicon Revision
SonicSR equ (28h * 2)
;Receiver Resource Area
rra_struc struc
rra_ptr dd ?
rra_cnt dd ?
rra_struc ends
;Receiver Descriptor Area
rda_struc struc
rda_status dw ?
rda_byte_count dw ?
rda_ptr dd ?
rda_seq_number dw ?
rda_link dw ?
rda_in_use dw ?
rda_backlink dw ? ;for our own purposes, not SONIC's.
rda_forelink dw ? ;for our own purposes, not SONIC's.
rda_struc ends
; Transmit Descriptor structure
tda_struc struc
tda_status dw ?
tda_config dw ?
tda_size dw ?
tda_frag_count dw ?
tda_frag_ptr dd ?
tda_frag_size dw ?
tda_link dw ?
tda_struc ends
; Content-Addressible Memory descriptor.
cam_struc struc
cam_entry_ptr dw 0
cam_addr0 dw 0
cam_addr1 dw 0
cam_addr2 dw 0
cam_struc ends
dds_struc struc
dds_size dd 0 ;region size.
dds_offset dd 0 ;offset (using 32 bits)
dds_seg dw 0 ;segment or selector
dds_buffer_id dw 0
dds_physical dd 0 ;physical address
dds_struc ends
;MAX8023LENGTH equ 1514
MAX8023LENGTH equ 1520
code segment para 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_addr dw -1,-1
port_addr dw ?
dma_no db ?
public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db BLUEBOOK, IEEE8023, 0 ;from the packet spec
driver_type db 89 ;from the packet spec
driver_name db 'TCENET',0 ;name of the driver.
driver_function db 2 ;basic, extended
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 RECEIVE_BUF_COUNT-1 ;(# of back-to-back MTU rcvs) - 1
dw TRANSMIT_BUF_COUNT-1 ;(# of successive xmits) - 1
int_num dw 0 ;Interrupt # to hook for post-EOI
;processing, 0 == none,
align 2
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 rcv_mode_2 ;only ours.
dw rcv_mode_3 ;ours plus broadcast
dw 0 ;some multicasts
dw rcv_mode_5 ;all multicasts
dw rcv_mode_6 ;all packets
my_dds dds_struc<>
vds_active db ? ;<>0 if memory mapping is on.
tdaptr dw tda1 ;-> the TDA's we're using.
rraptr dw rra1
camptr dw cam1 ;-> the CAM we're using.
camcount dw 1 ;always has our address.
rdaptr dw rda1
next_rda dw ?
include timeout.asm
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 ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
assume ds:nothing
mov bx,tdaptr
mov ax,18
call set_timeout
loadport
sncport SonicCR
send_pkt_1:
in ax,dx
test ax,TXP
je send_pkt_2 ;zero means yes, done transmitting.
; test code:[bx].tda_status,TXDONE ;Is the SONIC done transmitting it?
; jne send_pkt_2 ;any non-zero means yes.
call do_timeout
jne send_pkt_1
mov dh,CANT_SEND
stc
ret
send_pkt_2:
mov ax,cx ;store the count.
cmp ax,RUNT ; minimum length for Ether
ja oklen
mov ax,RUNT ; make sure size at least RUNT
oklen:
mov code:[bx].tda_frag_size,cx
mov code:[bx].tda_size,cx
mov code:[bx].tda_config,0
mov ax,code:[bx].tda_frag_ptr.offs ;store the packet.
mov dx,code:[bx].tda_frag_ptr.segm
call phys_to_segmoffs
call movemem
lea di,code:[bx].tda_status ;get the address of the TDA.
movseg es,cs
call segmoffs_to_phys
loadport ;point the CTDA to the TDA.
sncport SonicCTDA
out dx,ax
sncport SonicCR ;command SONIC to transmit.
mov ax,TXP
out dx,ax
clc
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
mov cx,EADDR_LEN
loadport ; Get our Ethernet address base.
setport EBASE
cld
get_address_1:
insb ; get a byte of the eprom address
inc dx ; next register
loop get_address_1 ; go back for rest
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:
movseg es,cs
mov di,camptr ;point di to our Ethernet adddr.
add di,2
rep movsb
movseg ds,cs
assume ds:code
call load_cam ;initialize with our new address.
mov dh,CANT_SET ;just in case.
jc set_address_done
set_address_okay:
mov cx,EADDR_LEN ;return their address length.
clc
set_address_done:
ret
load_cam:
loadport
sncport SonicCR
load_cam_1:
in ax,dx ;wait for transmit done.
test ax,TXP
jne load_cam_1 ;nonzero means still transmitting.
movseg es,ds
mov di,camptr ;point the Descriptor Pointer Register
call segmoffs_to_phys
sncport SonicCDP ; to our CAM area.
out dx,ax
pause_
sncport SonicCDC ;load the CAM descriptor table length.
mov ax,camcount
out dx,ax
pause_
sncport SonicCR
mov ax,LCAM ;load the CAM.
out dx,ax
ret
rcv_mode_2:
mov ax,0 ;accept only mine
jmp short rcv_mode_set
rcv_mode_3:
mov ax,BRD ;accept mine + broadcast
jmp short rcv_mode_set
rcv_mode_5:
mov ax,AMC ;accept any multicast frames.
jmp short rcv_mode_set
rcv_mode_6:
mov ax,RNT or PRO or BRD or AMC ;promiscuous mode (runts also)
rcv_mode_set:
loadport
sncport SonicRCR
out dx,ax
pause_
mov ax,RXEN ;enable reception.
jmp short rcv_mode_set_1
rcv_mode_1:
mov ax,RXDIS ;disable the receiver.
rcv_mode_set_1:
loadport ;enable or disable reception.
sncport SonicCR
out dx,ax
pause_
ret
public set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, ax = number of addresses,
; cx = address byte count.
;return nc if we set all of them, or cy,dh=error if we didn't.
mov cx,ax
inc ax ;one more for our Ethernet address.
mov camcount,ax
mov ax,1
mov di,camptr
add di,8 ;skip first entry (our address).
set_multicast_list_1:
stosw ;store the CAM entry number.
movsw ;move the address over.
movsw
movsw
loop set_multicast_list_1
mov cx,camcount ;compute the enable mask.
mov ax,-1 ;shift zeroes in, then turn them
shl ax,cl
not ax ; into ones.
stosw ;store the enable mask.
call load_cam ;now stick it to the hardware.
mov dh,NO_MULTICAST ;for some reason we can't do multi's.
stc
ret
public terminate
terminate:
loadport
sncport SonicCR
mov ax,RST or STP or RXDIS ;reset the sonic, stop timer, stop rcv.
out dx,ax
;This routine will remove the (host) DMA controller from
;cascade mode of operation.
mov al,dma_no
or al,SET_DMA_MASK
cmp dma_no,4 ;If channel 5 or 6,
ja terminate_16 ; use sixteen bit dma.
terminate_8:
out DMA_8MASK_REG,al
jmp short terminate_done
terminate_16:
out DMA_16MASK_REG,al
terminate_done:
; unlock the dma block.
mov di,offset my_dds
mov ax,cs
mov es,ax
mov dx,0
mov ax,8104h
int 4bh
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
;call this routine to schedule a subroutine that gets run after the
;recv_isr. This is done by stuffing routine's address in place
;of the recv_isr iret's address. This routine should push the flags when it
;is entered, and should jump to recv_exiting_exit to leave.
;enter with ax = address of routine to run.
extrn schedule_exiting: near
;recv_exiting jumps here to exit, after pushing the flags.
extrn recv_exiting_exit: near
;enter with dx = amount of memory desired.
;exit with nc, dx -> that memory, or cy if there isn't enough memory.
extrn malloc: 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
loadport
sncport SonicIMR
in ax,dx
pause_
mov cx,ax ;remember the interrupt mask.
xor ax,ax ;clear interrupt requests.
out dx,ax
pause_
sncport SonicISR
in ax,dx ;now match the IMR against the ISR.
and cx,ax
jnz recv_1 ;if they overlap, then we have a real interrupt.
ret ;spurious interrupt.
recv_1:
;check out and get rid of interrupt causes.
test ax,PKTRX ;packet received?
jne recv_2 ;yes.
jmp recv_rbe ;no, go see if we ran out of buffers.
recv_2:
and ax,PKTRX or RBAE ;clear the PKTRX and RBAE flags.
out dx,ax
; handle received packet
recv_do_next:
mov bx,next_rda ;point to the rda that the sonic filled.
cmp [bx].rda_in_use,0 ;did they really fill it?
je recv_do_next_1 ;yes.
jmp recv_rbe
recv_do_next_1:
if 0
cmp TossRxFlag,0 ;should we toss this one?
je recv_do_next_2 ;no.
mov TossRxFlag,0 ;yes, just toss it.
jmp recv_err
recv_do_next_2:
endif
test ax,RBAE ;did we exceed the size of the buffer?
je recv_do_next_3 ;no.
;;; inc ReceiveBufferExceeded ;yes.
jmp recv_err
recv_do_next_3:
mov ax,[bx].rda_status
test ax,CRCR ;checksum error?
je recv_do_next_4 ;no.
;;; inc CRCErrors
jmp recv_err
recv_do_next_4:
test ax,FAER ;alignment error?
je recv_do_next_5 ;no.
;;; inc FrameAlignmentErrors
jmp recv_err
recv_do_next_5:
test ax,PRX ;otherwise okay?
jne recv_do_next_6 ;yes.
jmp recv_err
recv_do_next_6:
; get logical address of the buffer.
mov cx,[bx].rda_byte_count ; bp := # of bytes received
sub cx,4 ;leave the FCS behind.
mov ax,[bx].rda_ptr.offs
mov dx,[bx].rda_ptr.segm
call phys_to_segmoffs ;make es:di -> packet.
push bx
push es
push di
add di,EADDR_LEN+EADDR_LEN ;skip the ethernet addreses and
; point to the packet type.
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:
push cx
call recv_find
pop cx
pop si
pop ds
pop bx
assume ds:nothing
mov ax,es ;is this pointer null?
or ax,di
je recv_free ;yes - just free the frame.
push es
push di
push cx
call movemem
pop cx
pop si
pop ds
assume ds:nothing
call recv_copy
jmp short recv_free
recv_err:
call count_in_err
recv_free:
movseg ds,cs
assume ds:code
test [bx].rda_status,LPKT ;is it the last packet in the RBA?
je recv_not_last ;no, go deal with it.
push bx ;we filled up all the RDAs, have
call update_rwp ; to clean up.
pop bx
recv_not_last:
or [bx].rda_link,EOL ; this RDA is now new EOL.
mov [bx].rda_in_use,0ffffh ; sonic now has this one.
mov si,[bx].rda_backlink
and [si].rda_link,not EOL ;clear EOL in previous RDA.
sncport SonicCR
mov ax,RXEN
out dx,ax ; re-enable Receive
mov bx,[bx].rda_forelink ; now move to next rda.
mov next_rda,bx
sncport SonicISR ; setup for next check
in ax,dx
jmp recv_do_next
recv_rbe:
;we get here with dx = SonicISR
in ax,dx ; get interrupt status
test ax,RBE ; is it a Receive Buf Exhaust?
jz recv_txdn
; SONIC thinks its out of receive bufs. ReRead the RDA
; inc RxBufferExhaustedCount
call update_rwp
loadport
sncport SonicISR
mov ax,RBE
out dx,ax ; clr int & reread the RDA
pause_
recv_txdn:
;we get here with dx = SonicISR
in ax,dx
and ax, (TXDN or TXER) ;done sending pkts?
jz recv_txdn_1 ;go if not.
pause_
out dx,ax ;clear this cause.
recv_txdn_1:
;we get here with dx = SonicISR
; cli
add dx,-2 ;point dx to SonicIMR
mov ax,enabled_IMR ;re-enable our ints.
out dx,ax
ret
update_rwp:
loadport
sncport SonicRSA
in ax,dx
pause_
mov si,ax ;get the RSA into si.
sncport SonicREA
in ax, dx
pause_
mov di,ax ;get the REA into di.
sncport SonicRWP
in ax,dx ; get RWP
add si,(RECEIVE_BUF_COUNT * (size rra_struc))/2
; assume we'll set @ 1/2
cmp ax,di ; if at end set to 1/2
je update_rwp_1
mov si,di ; else its at 1/2 - set to end
update_rwp_1:
mov ax,si
out dx,ax ; update RWP
pause_
ret
phys_to_segmoffs:
;enter with dx:ax as the physical address of the buffer,
;exit with es:di -> buffer.
cmp vds_active,0
jne phys_to_segmoffs_1
shl dx,16-4 ;move the upper four bits into position.
mov di,ax ;now get the low 12 bits of the segment.
shr di,4
or dx,di ;combine them.
mov es,dx
mov di,ax
and di,0fh ;now compute the offset.
ret
phys_to_segmoffs_1:
sub ax,my_dds.dds_physical.offs ;make into physical addresses.
add ax,offset begin_dma
mov di,ax ;make dx:ax be offset into dma region.
movseg es,cs
ret
segmoffs_to_phys:
;enter with es:di -> buffer.
;exit with dx:ax as the physical address of the buffer,
cmp vds_active,0
jne segmoffs_to_phys_1
mov dx,es ;get the high 4 bits of the segment,
shr dx,16-4
mov ax,es ;and the low 12 bits of the segment.
shl ax,4
add ax,di ;add in the offset.
adc dx,0
ret
segmoffs_to_phys_1:
xor dx,dx ;make dx:ax be offset into dma region.
mov ax,di
sub ax,offset begin_dma
add ax,my_dds.dds_physical.offs ;make into physical addresses.
adc dx,my_dds.dds_physical.segm
ret
public timer_isr
timer_isr:
;if the first instruction is an iret, then the timer is not hooked
iret
;beginning of the area of memory that the SONIC knows about (and that we will
; have to lock down for DMA).
begin_dma label byte
;we have two tda's because one of them might cross a physical 64K boundary.
;If that happens, then we use the other.
align 2
tda1 db (TRANSMIT_BUF_COUNT * size tda_struc) dup(?)
align 2
tda2 db (TRANSMIT_BUF_COUNT * size tda_struc) dup(?)
;the rra and cam share a common upper 16 bits of address, therefore they
;must be on the same 64K physical segment. There's also alignment
;considerations.
align 2
rra1 dw RECEIVE_BUF_COUNT * 4 dup(?)
cam1 dw 0 ;our address is always zero.
db EADDR_LEN dup(?) ;leave room for our address.
dw 1 ;for the CAM Enable register.
db (EADDR_LEN+2)*15 dup(?) ;leave room for up to 15 more.
RRA1SIZE equ $ - rra1
align 2
rra2 dw RECEIVE_BUF_COUNT * 4 dup(?)
cam2 dw 0 ;our address is always zero.
db EADDR_LEN dup(?) ;leave room for our address.
dw 1 ;for the CAM Enable register.
db (EADDR_LEN+2)*15 dup(?) ;leave room for up to 15 more.
;we have two rda's because one of them might cross a physical 64K boundary.
;If that happens, then we use the other.
align 2
rda1 db (RDA_COUNT * size rda_struc) dup(?)
align 2
rda2 db (RDA_COUNT * size rda_struc) dup(?)
;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
align 4 ;just for efficiency's sake.
end_resident label byte
db (RECEIVE_BUF_COUNT*RECEIVE_BUF_SIZE) + (TRANSMIT_BUF_COUNT*TRANSMIT_BUF_SIZE) dup(?)
end_free_mem label byte
;end of the area of memory that the SONIC knows about (and that we will
; have to lock down for DMA).
public usage_msg
usage_msg db "usage: tcenet [options] <packet_int_no> <int_no> <io_addr>",CR,LF,'$'
no_board_msg db "No tcenet detected.",CR,LF,'$'
io_addr_funny_msg label byte
db "No tcenet detected, continuing anyway.",CR,LF,'$'
bad_reset_msg db "Unable to reset the tcenet.",CR,LF,'$'
bad_init_msg db "Unable to initialize the tcenet.",CR,LF,'$'
vds_ver_msg db "Using VDS version ",'$'
public copyright_msg
copyright_msg db "Packet driver for a Thomas-Conrad tcenet, version ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,CR,LF
db '$'
io_addrs dw 100h,120h,140h,160h,300h,320h,340h,0
io_addrs_end label word
int_nos db 2,3,5,7,10,11,12,15 ;interrupt numbers.
dma_nos db 1,5,6,7 ;dma channel numbers
our_address db 6 dup(?) ;temporarily hold our address
int_no_name db "Interrupt number ",'$'
io_addr_name db "I/O port ",'$'
extrn set_recv_isr: near
extrn maskint: near
;enter with si -> argument string, di -> dword 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
extrn decout: near
extrn chrout: near
;print a crlf
extrn crlf: near
public parse_args
parse_args:
;exit with nc if all went well, cy otherwise.
assume ds:code
mov di,offset int_no
call get_number
mov di,offset io_addr
call get_number
clc
ret
public etopen
etopen:
assume ds:code
cmp io_addr,-1 ;Did they ask for auto-detect?
je find_board
call detect_board ;no, just verify its existance.
je find_board_found
mov dx,offset io_addr_funny_msg
mov ah,9
int 21h
jmp find_board_found
find_board:
mov bx,offset io_addrs ;Search for the Ethernet address.
mov io_addr+2,0
find_board_0:
mov ax,[bx]
mov io_addr,ax
call detect_board
je find_board_found
find_board_again:
add bx,2 ;not at this port, try another.
cmp bx,offset io_addrs_end ;last address?
jb find_board_0
mov dx,offset no_board_msg ;Tell them that we can't find it.
mov ah,9
int 21h
stc
ret
find_board_found:
loadport
sncport SonicCR
mov ax,RST or STP or RXDIS ;reset the sonic, stop timer, stop rcv.
out dx,ax
pause_
setport CONFIG ;get the configuration register and
in ax,dx ; determine the interrupt number.
and ax,7
mov bx,ax
mov al,int_nos[bx]
mov int_no,al
;This routine will put the (host) DMA controller into
;cascade mode of operation.
in ax,dx ;get the dma channel field.
shr ax,3
and ax,3
mov bx,ax
mov al,dma_nos[bx]
mov dma_no,al
mov ah,al ;save a copy.
and al,DMA_CHANNEL_FIELD
or al,SET_DMA_MASK
cmp ah,4 ;If channel 5 or 6,
ja dma_16 ; use sixteen bit dma.
dma_8:
out DMA_8MASK_REG,al ;mask the channel off.
mov al,ah
or al,CASCADE_MODE ;put it into cascade mode.
out DMA_8MODE_REG,al
mov al,ah ;turn the channel on.
out DMA_8MASK_REG,al
jmp short dma_done
dma_16:
and ah,DMA_CHANNEL_FIELD ;turn off the extra bits.
out DMA_16MASK_REG,al ;mask the channel off.
mov al,ah
or al,CASCADE_MODE ;put it into cascade mode.
out DMA_16MODE_REG,al
mov al,ah ;turn the channel on.
out DMA_16MASK_REG,al
dma_done:
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.
mov al,int_no
call maskint ;disable these interrupts.
loadport
sncport SonicCR
xor ax,ax ;take sonic out of reset.
out dx,ax
pause_
sncport SonicDCR
mov ax,PO1 or USR1 or WC1 or WC0 or BMS or RFT1 or TFT0
out dx,ax
pause_
sncport SonicRCR
mov ax,BRD ; accept broadcast
out dx,ax
pause_
sncport SonicTCR
;;;voodoo programming -- these bits are read-only.
;;; mov ax,04eh ; national's value
xor ax,ax ;normal Ethernet.
out dx,ax
pause_
sncport SonicIMR
xor ax,ax ; disable all, for now.
out dx,ax
pause_
sncport SonicISR
mov ax,-1 ; clear all
out dx,ax
pause_
;
; now we lock down the memory that we'll be using for DMA. That is,
; assuming we're running under some sort of memory mapper.
;
mov ax,40h ;is some program providing VDS?
mov es,ax
test byte ptr es:[7bh],20h
jz memory_locked ;no.
mov ax,08102h ;get version information.
mov dx,1010b ;no auto-remap, copy data into buffer.
int 4bh
cmp ax,8102h ;did it change into version number?
je memory_locked ;no, must not be there...
push ax
mov dx,offset vds_ver_msg
mov ah,9
int 21h
pop ax
push ax
mov al,ah ;print major version number.
xor ah,ah
xor dx,dx
call decout
mov al,'.'
call chrout
pop ax ;print minor version number.
xor ah,ah
xor dx,dx
call decout
call crlf
mov vds_active,1
movseg es,cs
mov di,offset my_dds
mov dx,0
call malloc
sub dx,offset begin_dma
mov [di].dds_size.offs,dx
mov [di].dds_size.segm,0
mov [di].dds_offset.offs,offset begin_dma
mov [di].dds_offset.segm,0
mov [di].dds_seg,cs
mov [di].dds_buffer_id,0
mov ax,08103h ;request that the memory be locked.
mov dx,1010b ;no auto-remap, copy data into buffer.
int 4bh
memory_locked:
;
; We need to keep the all the TDAs in the same physical 64K segment. So, if the
; first crosses a segment, the second cannot, so we'll use it.
;
mov di,offset tda1
movseg es,cs
call segmoffs_to_phys
add ax,TRANSMIT_BUF_COUNT * (size tda_struc)
jnc no_tda_overflow
mov di,offset tda2
no_tda_overflow:
mov tdaptr,di ;remember where the tda's are.
mov cx,TRANSMIT_BUF_COUNT
mov bx,di
init_tdas:
mov dx,TRANSMIT_BUF_SIZE
call malloc
mov di,dx
mov [bx].tda_size,0
mov [bx].tda_status,0
mov [bx].tda_frag_count,1 ;packet driver spec only allows one.
mov [bx].tda_link,EOL
call segmoffs_to_phys ;convert es:di into physical address.
mov [bx].tda_frag_ptr.offs,ax ;store the packet's physical address.
mov [bx].tda_frag_ptr.segm,dx
add bx,(size tda_struc)
loop init_tdas
; init transmit regs UTDA
mov di,tdaptr
call segmoffs_to_phys ; get physical
push dx ;save the high word.
loadport
sncport SonicUTDA
pop ax ; hi word phys
out dx,ax
;
; We need to keep the all the RDAs in the same physical 64K segment. So, if the
; first crosses a segment, the second cannot, so we'll use it.
;
mov si,offset rda1
mov ax,si
call segmoffs_to_phys
add ax,RDA_COUNT * (size rda_struc)
jnc no_rda_overflow
mov si,offset rda2
no_rda_overflow:
mov rdaptr,si ;remember which one we're using
mov next_rda,si
mov di,rdaptr
mov cx,RDA_COUNT
xor si,si
init_rda_1:
push di
add di,(size rda_struc)
call segmoffs_to_phys ;get physical offset of next link.
pop di
mov [di].rda_link,ax ;make a pointer to the next.
mov [di].rda_backlink,si ;make a pointer to the previous.
mov [di].rda_in_use,0ffffh ;set ownership to SONIC.
lea ax,[di]+(size rda_struc)
mov [di].rda_forelink,ax ;make a pointer to the next.
mov si,di ;remember where we were.
add di,(size rda_struc) ;point di to next.
loop init_rda_1
mov di,rdaptr
mov [si].rda_forelink,di ;succ(end) = begin.
mov [di].rda_backlink,si ;prev(begin) = end.
call segmoffs_to_phys ;get physical offset of first link.
mov [si].rda_link,ax
or [si].rda_link,EOL ;mark this one as the last.
;set SONIC receive descriptor area pointers.
push dx ;save upper word.
loadport
sncport SonicCRDA ;set current
out dx,ax
pause_
sncport SonicURDA ;set upper.
pop ax ;restore upper word (pushed as dx).
out dx,ax
; Init the RRA. The RRA and CAM share the same upper address bits, so they,
; like all the TDAs and RDAs, must be in the same physical segment.
mov si,offset rra1
mov ax,si
call segmoffs_to_phys ;get the physical address
add ax,RRA1SIZE ;fits in one segment?
jnc no_rra_overflow ;yes.
mov si, offset rra2 ;no, the second *must*.
no_rra_overflow:
mov rraptr,si ;save the pointer to the right rra.
add si,cam1-rra1 ;compute the pointer to the CAM.
mov camptr,si
mov bx,rraptr ;point di to our rra.
mov cx,RECEIVE_BUF_COUNT
InitRRALoop:
mov dx,RECEIVE_BUF_SIZE
call malloc
mov di,dx
call segmoffs_to_phys ; get physical
mov [bx].rra_ptr.offs,ax ;low word address
mov [bx].rra_ptr.segm,dx ;high word address
mov [bx].rra_cnt.offs,MAX8023LENGTH ;count low word
mov [bx].rra_cnt.segm,0 ;count high word.
add bx,(size rra_struc)
loop InitRRALoop
loadport
sncport SonicEOBC
mov ax, (MAX8023LENGTH)/2 ;count of words.
out dx,ax
pause_
mov di,rraptr ;init the pointers.
call segmoffs_to_phys
push ax ;preserve the low word.
mov ax,dx
loadport
sncport SonicURRA
out dx,ax ;high word.
pause_
pop ax
sncport SonicRSA ;output the low word.
out dx,ax
sncport SonicRRP
out dx,ax
add ax,cam1-rra1 ;point to the end of the RRA.
sncport SonicREA
out dx,ax ;set the end pointer.
setport SonicRWP
out dx,ax ; write reg gets it
; Init SONIC internal counters
mov ax,-1 ;clear registers (inverted on write)
sncport SonicCRCT
out dx,ax ;CRC Tally
sncport SonicFAET
out dx,ax ;FAE Tally
sncport SonicMPT
out dx,ax ;Missed Packet Tally
;take SONIC out of reset.
sncport SonicCR
xor ax,ax
out dx,ax
pause_
mov ax, RRRA ;Start the RRA.
out dx,ax
; get our address out of ROM, and stuff it into the CAM.
movseg es,ds
mov di,offset our_address
mov cx,EADDR_LEN
call get_address
mov si,offset our_address
mov cx,EADDR_LEN
call set_address
loadport
sncport SonicCR
mov ax,RXEN ;Enable receive.
out dx,ax
sncport SonicIMR ;Enable interrupts.
mov ax,enabled_IMR
out dx,ax
call set_recv_isr
clc
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 io_addr
mov dx,offset io_addr_name
call print_number
ret
;TCC's manufacturer prefix is 13:80:00. They've also used the token
;ring version of the prefix (bit-reversed) , which is 00:01:8c. Oops.
detect_board:
;test to see if a board is located at io_addr.
;return nz if not.
loadport
setport EBASE
in ax,dx
setport EBASE+2
cmp ax,00h + 01h*256
jne detect_board_1
in al,dx
cmp al,0c8h
ret
detect_board_1:
cmp ax,13h + 80h*256
jne detect_board_exit
in al,dx
cmp al,0
detect_board_exit:
ret
code ends
end