home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Media Share 9
/
MEDIASHARE_09.ISO
/
network
/
pcb121.zip
/
3C507.INC
next >
Wrap
Text File
|
1992-01-23
|
40KB
|
1,133 lines
;;******************************************************************************
;; 3c507.inc 3c507.inc
;;******************************************************************************
;;
;; Copyright (C) 1991 Vance Morrison
;;
;;
;; Permission to view, compile, and modify for LOCAL (intra-organization)
;; USE ONLY is hereby granted, provided that this copyright and permission
;; notice appear on all copies. Any other use by permission only.
;;
;; Vance Morrison makes no representations about the suitability
;; of this software for any purpose. It is provided "as is" without expressed
;; or implied warranty. See the copywrite notice file for complete details.
;;
;;******************************************************************************
;; 3c507.inc holds the interface routines for the 3com etherlink 16 card.
;;
;; The functions provided by this file are
;;
;; C507_DECLARE name, io_address, seg, len, promiscuous
;; C507_DEFINE_out_AX name, fail
;; C507_IF_R_ACCESS_out_BX_CX_ES name, no_packet
;; C507_IF_R_CONT_in_BX_CX_ES_const_BX_CX_DX_BP_SI_DI_ES name, ok
;; C507_IF_R_FREE_const_BX_CX_BP_SI_DI_ES name
;; C507_IF_W_ACCESS_in_CX_out_DI_ES_const_BX_CX_BP name, no_buffer
;; C507_IF_W_WRITE_in_CX_const_BX_BP_ES name
;; C507_IF_SET_ADDRESS_in_SI_const_BX_CX_BP_DI_ES name
;; C507_IF_COPY_in_CX_SI_DI_ES_out_SI_DI_const_BX_BP_ES name
;;
;; Variables set by this module
;;
;; c507_&name&_declared ;; one if this interface exists
;; if_&name&_address ;; the hardware address
;; if_&name&_mtu ;; the maximum trans unit
;;
;;******************************************************************************
;; data storage needed by this module
c507_data STRUC
;; these values are set at init only
c507_base dw 0 ;; the offset value for shared memory
c507_end_buff dw 0 ;; end of buffer block for packet data
c507_start_buff dw 0 ;; begining of buffer block for packet data
;; set at init, and updated at FREE
c507_last_rbd dw 0 ;; points to buff desc with the EOL marker
c507_last_rfd dw 0 ;; points to frame desc with the EOL marker
;; set at R_ACCESS used at FREE
c507_last_frame_rbd dw 0 ;; last buff descriptor in current frame
c507_cur_frame dw 0 ;; pointer to current frame
c507_cur_tcb dw 0 ;; current transmit command buffer
c507_data ENDS
;;******************************************************************************
;; C507_DECLARE name, io_address, seg, len, promiscuous
;; declares that there is a 3c507 ethernet card at 'io_address'. The
;; shared memory starts a the SEGMENT 'seg' and has length 'len'.
;; if 'promiscuous' is non-blank and non-zero then every packet on
;; the ethernet is returned.
;;
c507_first = 0 ;; the first 3c507 card.
C507_DECLARE MACRO name, io_address, seg, len, promiscuous
.errb <seg>
.errb <len>
.DATA
c507_&name&_declared = 1
c507_&name&_io = io_address
c507_&name&_seg = seg
c507_&name&_len = len
c507_&name&_promiscuous = 0
ifnb <promiscuous>
c507_&name&_promiscuous = 0&promiscuous
endif
if (name le c507_first) or (c507_first eq 0)
c507_first = name
endif
if_&name&_mtu = 1514
global c507_&name&_data:c507_data
global if_&name&_address:word
.CODE
global c507_&name&_real_define:near
ENDM
;;******************************************************************************
;; IF_DEFINE name
;; sets asside memory an name object and initializes it. This
;; routine is a no-op if 'name' was not declared
;; Note that if multiple 3C507 cards are present, they must be
;; the smallest named card must be defined first (since this card
;; turns on all the cards). AX holds a failure code if the initialization
;; fails.
;;
C507_DEFINE_out_AX MACRO name, fail
ifdef c507_&name&_declared
call c507_&name&_real_define
or AX, AX
jnz fail
endif
ENDM
C507_REAL_DEFINE MACRO name
local ret_code, rfd_loop, found_rfd, rbd_loop, found_rbd
.errb <name>
ifdef c507_&name&_declared
.DATA
if_&name&_address DW 3 dup (0)
c507_&name&_data c507_data <>
.CODE
c507_&name&_real_define:
;; put all card in run state (only done for first card)
if name eq c507_first
xor AX, AX
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES C507_ID_PORT, 0
C507_SEND_ID_const_BX_BP_SI_DI_ES C507_ID_PORT
xor AX, AX
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES C507_ID_PORT, 0
endif
;; get the ethernet address
mov SI, offset if_&name&_address
C507_GET_ADDRESS_in_SI_const_BX_BP_DI_ES c507_&name&_io
C507_INIT_SETUP_out_ES c507_&name&_io, c507_&name&_seg, c507_&name&_len, if_&name&_address, c507_&name&_promiscuous, ret_code
mov c507_&name&_data.c507_base, ES
;; initialize my state variables
mov SI, ES:[SCB+scb_rframes]
mov c507_&name&_data.c507_cur_frame, SI
mov DI, ES:[SI+rfd_rbd] ;; DI points to the first buffer descriptor, we
;; assume that this rbd points to the begining
;; of the large block of memory
mov DX, ES:[DI+rbd_buff.offs]
mov c507_&name&_data.c507_start_buff, DX
;; find the last element of the Buffer list
rbd_loop:
test ES:[DI+rbd_len], RBD_LEN_EOL
jnz found_rbd
mov DI, ES:[DI+rbd_next]
jmp rbd_loop
found_rbd:
mov c507_&name&_data.c507_last_rbd, DI
mov AX, ES:[DI+rbd_len] ;; DI points to the last buffer descriptor. we
;; assume that this rbd points to the end of the
;; large block of memory.
and AX, RBD_LEN_MSK
mov DX, ES:[DI+rbd_buff.offs]
add DX, AX
mov c507_&name&_data.c507_end_buff, DX
;; find the last element of the Frame list
mov DI, ES:[SCB+scb_rframes]
rfd_loop:
test ES:[DI+rfd_cmd], RFD_CMD_EOL
jnz found_rfd
mov DI, ES:[DI+rfd_next]
jmp rfd_loop
found_rfd:
mov c507_&name&_data.c507_last_rfd, DI
mov c507_&name&_data.c507_cur_tcb, CU_TCMD1
xor AX, AX
ret_code:
ret
endif
ENDM
;;******************************************************************************
;; IF_R_ACCESS_out_BX_ES name, no_packet
;; IF_R_ACCESS waits for the next packet to come from the the board
;; associated with 'name' and returns a pointer to the begining of
;; an ethernet packet in BX:ES. CX holds the length of the packet
;; R_ACCESS jumps to 'no_packet' if there are no packets waiting to
;; be read in
;;
C507_IF_R_ACCESS_out_BX_CX_ES MACRO name, no_packet
local look_packet, found_packet, len_loop, last_buff
.errb <no_packet>
mov ES, c507_&name&_data.c507_base
mov SI, c507_&name&_data.c507_cur_frame
look_packet:
mov AX, ES:[SI+rfd_status]
test AX, RFD_STAT_DONE
jz no_packet
test AX, RFD_STAT_OK
jnz found_packet
mov SI, ES:[SI+rfd_next]
jmp look_packet
found_packet:
mov c507_&name&_data.c507_cur_frame, SI
mov SI, ES:[SI+rfd_rbd]
mov BX, ES:[SI+rbd_buff.offs]
;; compute the length of the packet, as well as if it wrapped or not
xor CX, CX
len_loop:
mov AX, ES:[SI+rbd_used_len]
test AX, RBD_USED_LEN_EOF
jnz last_buff
and AX, RBD_USED_LEN_MSK
add CX, AX
mov SI, ES:[SI+rbd_next]
jmp len_loop
last_buff:
and AX, RBD_USED_LEN_MSK
add CX, AX
mov c507_&name&_data.c507_last_frame_rbd, SI
ENDM
;;******************************************************************************
;; IF_R_FREE_const_BX_CX_BP_SI_DI_ES name
;; After the client is through processing the packet returned by
;; IF_R_ACCESS, IF_R_FREE must be called to inform 'name' that the
;; memory that the packet was in can be reused for future packets.
;;
C507_IF_R_FREE_const_BX_CX_BP_SI_DI_ES MACRO name
local ok_rbd, do_restart, no_restart, look_first_ndone, found_restart
.errb <name>
mov DX, SI ;; save SI
mov AX, ES
xchg AX, c507_&name&_data.c507_base ;; load/save ES
mov ES, AX
mov SI, c507_&name&_data.c507_last_frame_rbd
or ES:[SI+rbd_len], RBD_LEN_EOL
mov SI, c507_&name&_data.c507_last_rbd
and ES:[SI+rbd_len], (NOT RBD_LEN_EOL)
mov SI, c507_&name&_data.c507_last_frame_rbd
mov c507_&name&_data.c507_last_rbd, SI
mov SI, c507_&name&_data.c507_cur_frame
mov AX, SI
mov ES:[SI+rfd_status], 0
or ES:[SI+rfd_cmd], RFD_CMD_EOL
mov SI, ES:[SI+rfd_next]
mov c507_&name&_data.c507_cur_frame, SI
mov SI, c507_&name&_data.c507_last_rfd
mov ES:[SI+rfd_cmd], 0
mov c507_&name&_data.c507_last_rfd, AX
mov AX, ES:[SCB+scb_status] ;; is the reciever still recieving?
and AX, SCB_STAT_RUS_MSK
cmp AX, SCB_STAT_RUS_READY
jz no_restart
mov SI, c507_&name&_data.c507_cur_frame
mov AX, 16 ;; only look forward 16 packets at most
look_first_ndone:
test ES:[SI+rfd_status], RFD_STAT_DONE
jz found_restart
mov SI, ES:[SI+rfd_next]
dec AX
jnz look_first_ndone
found_restart:
mov ES:[SCB+scb_rframes], SI
mov ES:[SI+rfd_status], 0
cmp word ptr ES:[SI+rfd_rbd], 0FFFFH
jnz ok_rbd
mov SI, c507_&name&_data.c507_last_rfd
mov AX, ES:[SI+rfd_rbd]
mov SI, ES:[SCB+scb_rframes]
mov ES:[SI+rfd_rbd], AX
ok_rbd:
mov ES:[SCB+scb_cmd], SCB_RUC_START
;; signal the 82586
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES c507_&name&_io, C507_IO_ATTN
no_restart:
mov SI, DX ;; restore SI
mov AX, ES
xchg AX, c507_&name&_data.c507_base ;; restore ES
mov ES, AX
ENDM
;;******************************************************************************
;; C507_IF_R_CONT_in_BX_CX_ES name, ok
;; IF_R_CONT determines if the packet returned by R_READ in BX:ES
;; of length CX is continuous. If it is it jumps to 'ok' otherwise
;; it just returns
;;
C507_IF_R_CONT_in_BX_CX_ES_const_BX_CX_DX_BP_SI_DI_ES MACRO name, ok
.errb <ok>
mov AX, BX
add AX, CX
cmp AX, c507_&name&_data.c507_end_buff
jb ok
ENDM
;;******************************************************************************
;; IF_W_ACCESS_in_CX_out_DI_ES name, no_buffer
;; IF_W_ACCESS returns a pointer to an output buffer for a packet. The
;; pointer is returned in DI:ES. If the ouptut buffer is busy, this
;; routine will jump to 'no_buffer'. The output buffer min(CX, 1536)
;; bytes long
;;
C507_IF_W_ACCESS_in_CX_out_DI_ES_const_BX_CX_BP MACRO name, no_buffer
local wait_busy, buffer_free, com_accepted, wait_com_accept, nocarry
.errb <no_buffer>
;; we ignore CX and always return a buffer 1536 bytes long
mov ES, c507_&name&_data.c507_base
mov SI, c507_&name&_data.c507_cur_tcb
cmp ES:[SI+cu_status], 0 ;; has the RU done ANYTHING with stat
jnz com_accepted
;; it may be 0 if the CU hasn't even accepted the command
;; check for this and wait if necessary
xor DX, DX ;; so we don't loop forever
wait_com_accept:
test ES:[SCB+scb_cmd], SCB_CMD_CUC_MSK
jz com_accepted
dec DX
jnz wait_com_accept
com_accepted:
xor DX, DX ;; so we don't loop forever
wait_busy:
test ES:[SI+cu_status], CU_STAT_BUSY
jz buffer_free
dec DX
jnz wait_busy
buffer_free:
mov SI, ES:[SI+cu_params+trans_tbd]
mov DI, ES:[SI+tbd_buff.offs]
ENDM
;;******************************************************************************
;; IF_W_WRITE_in_CX name
;; IF_W_WRITE actually signals the ethernet board to write a packet to
;; the ethernet. The packet is assumed to be in the buffer returned by
;; IF_W_ACCESS. CX is the length of the packet to send.
;;
C507_IF_W_WRITE_in_CX_const_BX_BP_ES MACRO name
local not_tcmd1, check_prev_com, no_prev_com, wait_busy, buffer_free
.errb <name>
mov DX, ES ;; save ES
mov ES, c507_&name&_data.c507_base
mov DI, c507_&name&_data.c507_cur_tcb
;; make any previous command has been recognized before
;; sending this next one.
xor AX, AX
check_prev_com:
test ES:[SCB+scb_cmd], SCB_CMD_CUC_MSK
jz no_prev_com
dec AX
jnz check_prev_com
no_prev_com:
xor AX, AX
wait_busy:
test ES:[DI+cu_status], CU_STAT_BUSY
jz buffer_free
dec AX
jnz wait_busy
buffer_free:
mov ES:[DI+cu_status], 0
mov ES:[DI+cu_cmd], CU_CMD_EOL+CU_CMD_TRANS ;; probably unnecessary
mov ES:[DI+cu_params+trans_len], CX ;; probably unnecessary
mov SI, ES:[DI+cu_params+trans_tbd]
add CX, TBD_LEN_EOL
mov ES:[SI+tbd_len], CX
;; send our transmit command
mov SI, offset SCB
mov ES:[SI+scb_status], 0
mov ES:[SI+scb_cuc], DI
mov ES:[SI+scb_cmd], SCB_CUC_START
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES c507_&name&_io, C507_IO_ATTN
;; update the tcb pointer to the 'other' command block
mov AX, CU_TCMD1
cmp AX, DI
jnz not_tcmd1
mov AX, CU_TCMD2
not_tcmd1:
mov c507_&name&_data.c507_cur_tcb, AX
mov ES, DX ;; restore ES
ENDM
;;******************************************************************************
;; IF_SET_ADDRESS_in_SI name
;; IF_SET_ADDRESS_in_SI sets the hardware address to be the value
;; pointed to by SI. Note this function may be a no-op if the
;; hardware address cannot be set (ETHERNET for example)
;;
C507_IF_SET_ADDRESS_in_SI_const_BX_CX_BP_DI_ES MACRO name
.err ;; we don't support setting ethernet addresses (yet)
ENDM
;;******************************************************************************
;; IF_COPY_in_CX_SI_DI_ES_out_SI_DI name
;; IF_COPY_in_CX_SI_DI_ES copys a packet from the input buffer (pointed
;; to by SI and the segement register given in IF_DECLARE) to an output
;; buffer (pointed to by DI and dest_reg) of length CX. It assumes the
;; output buffer is contiguous. (and the caller shouln't care if the
;; input buffer is contiguous)
;;
C507_IF_COPY_in_CX_SI_DI_ES_out_SI_DI_const_BX_BP_ES MACRO name
local wrap, done
.errb <name>
mov DX, DS ;; save DS
mov AX, c507_&name&_data.c507_end_buff
sub AX, SI ;; space till end
cmp AX, CX
jbe wrap
inc CX
shr CX, 1
mov DS, c507_&name&_data.c507_base
rep movsw
jmp done
wrap:
xchg AX, CX
sub AX, CX
mov DS, c507_&name&_data.c507_base
inc CX
shr CX, 1
rep movsw
mov CX, AX
mov DS, DX
;; remember this instruction rely on DS
mov SI, c507_&name&_data.c507_start_buff
mov DS, c507_&name&_data.c507_base
inc CX
shr CX, 1
rep movsw
done:
mov DS, DX ;; restore DS
ENDM
;;******************************************************************************
;; These macros and definitions are specific to the 3C507
C507_ID_PORT = 100H
C507_IO_ADDR = 00H
C507_IO_CTR = 06H
C507_IO_INT_CLEAR = 0AH
C507_IO_ATTN = 0BH
C507_IO_ROM = 0DH
C507_IO_RAM = 0EH
C507_IO_INT = 0FH
;; values for teh IO_CTR reg
C507_CTR_RUN = 080H ;; 1 = run 0 = reset
C507_CTR_CA = 040H ;; obsolete way of to a chan attn
C507_CTR_LOOP = 020H ;; put in loopback
C507_CTR_LAD = 010H ;; LA address decode disable
C507_CTR_INT = 008H ;; interupt is pending (read only)
C507_CTR_IEN = 004H ;; interupt enable
C507_CTR_PG_MSK = 003H ;; controls first 6 locations
;; values for the CTR_PG field
C507_PG_3COM = 00H ;; page that has '*3com*' in it
C507_PG_ETHER = 01H ;; page ethernet address in it
;;*******************************************************************
;; This macro sends the ID sequence that the 3Com 3C507 responds to.
;; to the I/O address 'port'.
C507_SEND_ID_const_BX_BP_SI_DI_ES MACRO port
local id_loop, no_xor
mov CX, 0FFH
mov AL, 0FFH
id_loop:
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES port, 0
shl AL, 1
jnc no_xor
xor AL, 0E7H
no_xor:
loop id_loop
ENDM
;;*****************************************************************
;; this routine gets the ethernet address from the 3Com I/O registers
;; and places it in the buffer pointed to by SI
C507_GET_ADDRESS_in_SI_const_BX_BP_DI_ES MACRO port
local eaddr_loop
;; get the ethernet address
mov AL, C507_CTR_RUN+C507_PG_ETHER
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES port, C507_IO_CTR
mov CX, 6
mov DX, port+C507_IO_ADDR
eaddr_loop:
in AL, DX
mov [SI], AL
inc DX
inc SI
loop eaddr_loop
ENDM
;;********************************************************************
;; These definitions have more to do with the 82586
longptr struc ;; just allows me to get at 8086 long ptrs easily
offs dw ?
segm dw ?
longptr ends
;;*********************************************************************
;; This structure is always at a fixed location, and points to
;; the system control pointer
i82586_root struc ;; the root pointer
root_bus dw 0
root_zero db 4 dup (0)
root_scp dd ?
i82586_root ends
I82586_FIXED_ROOT = 0FFF6H ;; this is the fixed position
;;*********************************************************************
;; The system control pointer, in turn, points to the
;; system control block
i82586_scp struc ;; the system control pointer
scp_busy dw 0 ;; 82586 sets to 0 when reset complete
scp_scb dw 0 ;; offset to system control block
scp_base dd 0 ;; base for all 16 bit pointers
i82586_scp ends ;; the system control block
;;*********************************************************************
;; The system control block is the heart of the communication
;; between the CPU and the 82586. The 82586 has a control
;; unit (CU) which you can issue commands to, and a recieve
;; unit (RU) that buffers packets. Commands to these units
;; are given from this structure.
i82586_scb struc ;; the system control block
scb_status dw 0 ;; RU and CU status
scb_cmd dw 0 ;; RU and/or CU command
scb_cuc dw 0 ;; CU command (list)
scb_rframes dw 0 ;; buffers for RU
scb_crc_errs dw 0 ;; CRC error count
scb_aln_errs dw 0 ;; Alignment error count
scb_rcs_errs dw 0 ;; Resource error (queue filled)
scb_ovrn_errs dw 0 ;; Overruns (memory busy)
i82586_scb ends
;; masks for the scb_status field
SCB_STAT_CX = 08000H ;; command with interupt executed
SCB_STAT_FR = 04000H ;; Frame recieved
SCB_STAT_CNR = 02000H ;; command unit left active state
SCB_STAT_RNR = 01000H ;; receive unit left active state
SCB_STAT_CUS_MSK = 00700H ;; command status mask
SCB_STAT_RUS_MSK = 00070H ;; reciever status mask
;; these are the values for teh RUS_MSK field
SCB_STAT_RUS_IDLE = 00000H ;; reciever idle
SCB_STAT_RUS_SUSP = 00010H ;; reciever suspended
SCB_STAT_RUS_NORES = 00020H ;; reciever no_resources
SCB_STAT_RUS_READY = 00040H ;; reciever ready
;; masks for the scb_cmd (set by CPU cleared by 82586)
SCB_CMD_ACK_CX = 08000H ;; acks command executed
SCB_CMD_ACK_FR = 04000H ;; acks frame received
SCB_CMD_ACK_CNA = 02000H ;; acks CU not ready
SCB_CMD_ACK_RNR = 01000H ;; acks RU not ready
SCB_CMD_CUC_MSK = 00700H ;; CU command mask
SCB_CMD_RST = 00080H ;; reset the 82586
SCB_CMD_RUC_MSK = 00070H ;; RU command mask
;; values for the CUC_MSK part of the scb_cmd field
SCB_CUC_START = 00100H ;; start a CU command
SCB_CUC_RESUME = 00200H
SCB_CUC_SUSPEND = 00300H
SCB_CUC_ABORT = 00400H
;; values for the RUC_MSK part of the scb_cmd field
SCB_RUC_START = 00010H ;; start RU receiving packets
SCB_RUC_RESUME = 00020H
SCB_RUC_SUSPEND = 00030H
SCB_RUC_ABORT = 00040H
;;*********************************************************************
;; the scb_cuc points to a list of commands for the CU
;; to execute. Each entry in the list has this format
i82586_cu struc
cu_status dw 0 ;; status of this command
cu_cmd dw 0 ;; specifies the op
cu_next dw 0 ;; next cmd in the list
cu_params db 58 dup (0) ;; depends on particular cmd
i82586_cu ends
;; values for the cu_status field
CU_STAT_COMPLETE = 08000H ;; command is completed
CU_STAT_BUSY = 04000H ;; command not complete
CU_STAT_OK = 02000H ;; finished and OK
;; values for the cu_cmd field
CU_CMD_CMD_MSK = 00007H ;; mask for the command
CU_CMD_EOL = 08000H ;; last command list in list
CU_CMD_SUSPEND = 04000H ;; suspend after completion
CU_CMD_INT = 02000H ;; interupt after completion
;; values for the CMD_MSK parat of the cu_cmd field
CU_CMD_NOOP = 0 ;; noop command
CU_CMD_ADDRESS_SET = 1 ;; set ethernet address
CU_CMD_CONFIG = 2 ;; configure ethernet params
CU_CMD_MULTI_SET = 3 ;; set multicast addresses
CU_CMD_TRANS = 4 ;; transmit packet
CU_CMD_TDR = 5 ;; Time domain reflectometer
CU_CMD_DUMP = 6 ;; dump internal state
CU_CMD_DIAGNOSE = 7 ;; run diagnostics
;;*******************************************************************
;; these are the parameters of the TRAMSMIT command
i82586_trans struc
trans_tbd dw 0 ;; points to buffer descriptor
trans_dst_addr db 6 dup (0) ;; address to send it to (not used)
trans_len dw 0 ;; 802.3 length field (not used)
i82586_trans ends
;; the first parameter of a transmit command points to a list
;; of transmit buffer discriptors that characterize the
;; packet to send
i82586_tbd struc
tbd_len dw 0 ;; length and EOL flag
tbd_next dw 0 ;; pointer to next descriptor
tbd_buff dd 0 ;; a long pointer to the buffer
i82586_tbd ends
;; the bit fields bit in the tbd_len field
TBD_LEN_EOL = 08000H ;; Last buff in list
TBD_LEN_LEN_MSK = 03FFFH ;; this part is the length
;;*******************************************************************
;; the scb_rfd field points to a list of frame descriptors.
;; each of these describe a single packet
i82586_rfd struc
rfd_status dw 0 ;; recieve status
rfd_cmd dw 0 ;; really just specifies EOL
rfd_next dw 0 ;; next frame descriptor
rfd_rbd dw 0 ;; pointer to buff descriptor
rfd_dst_addr db 6 dup (0) ;; ethernet address of destination
rfd_src_addr db 6 dup (0) ;; ethernet address of source
rfd_len dw 0 ;; length of packet
i82586_rfd ends
RFD_STAT_DONE = 08000H
RFD_STAT_CONSUMED = 04000H
RFD_STAT_OK = 02000H
RFD_STAT_CRC = 00800H
RFD_STAT_ALN = 00400H
RFD_STAT_RSC = 00200H
RFD_STAT_OVRN = 00100H
RFD_STAT_RUNT = 00080H
RFD_CMD_EOL = 08000H ;; End of frame list
;; the rdb_buff field points to a buffer descriptor, which is
;; a unit of memory allocation.
i82586_rbd struc
rbd_used_len dw 0 ;; also hold some flag bits
rbd_next dw 0 ;; next buffer descriptor
rbd_buff dd 0 ;; pointer to actual memory
rbd_len dw 0 ;; length of buffer
i82586_rbd ends
;; masks for the rdb_used_len field
RBD_USED_LEN_EOF = 08000H ;; Last buff in a frame list
RBD_USED_LEN_VALID = 04000H ;; says the used_len field valid
RBD_USED_LEN_MSK = 03FFFH ;; this part is the used length
RBD_LEN_MSK = 03FFFH ;; this part is the used length
RBD_LEN_EOL = 08000H ;; End of buffer list
;;***********************************************************************
;; setup the chain of recieve buffers. SI points to a block
;; of memory 'len'*(SIZE i82586_rfd) bytes long. Note 1 < CX < 16K
C507_SETUP_R_FRAMES_in_CX_SI_ES_const_AX_BP_ES MACRO
local init_loop
;; create a circular list of receive buffers
mov DX, SI ;; save original pointer
mov DI, SI
add DI, (SIZE i82586_rfd)
dec CX
init_loop:
;; set up a frame descriptor
mov ES:[SI+rfd_status], 0
mov ES:[SI+rfd_cmd], 0 ;; says NOT end of list
mov ES:[SI+rfd_next], DI
mov ES:[SI+rfd_rbd], 0FFFFH
add SI, (SIZE i82586_rfd)
add DI, (SIZE i82586_rfd)
dec CX
jnz init_loop
mov ES:[SI+rfd_cmd], RFD_CMD_EOL ;; logical end of list
mov ES:[SI+rfd_next], DX ;; close the loop
mov ES:[SI+rfd_rbd], 0FFFFH
ENDM
;;***********************************************************************
;; setup the chain of recieve buffers. BX points to the begining of
;; a block of memory 'len'* CX bytes long and SI points to a block
;; of memory 'len'*(SIZE i82586_rbd) bytes long. Note 1 < CX < 16K
;; it returns in SI the start of the list of buffer descriptors.
C507_SETUP_R_BUFFS_in_BX_CX_SI_ES_out_SI_const_AX_BP_ES MACRO len
local init_loop
;; create a circular list of receive buffers
mov DX, SI ;; save original pointer
mov DI, SI
add DI, (SIZE i82586_rbd)
dec CX
init_loop:
;; set up a buffer descriptor
mov ES:[SI+rbd_next], DI
mov ES:[SI+rbd_buff.offs], BX
mov ES:[SI+rbd_buff.segm], 0
mov ES:[SI+rbd_len], len
add SI, (SIZE i82586_rbd)
add DI, (SIZE i82586_rbd)
add BX, len
dec CX
jnz init_loop
mov ES:[SI+rbd_next], DX ;; close the loop
mov ES:[SI+rbd_buff.offs], BX
mov ES:[SI+rbd_buff.segm], 0
mov ES:[SI+rbd_len], len+RBD_LEN_EOL ;; logical end of list
mov SI, DX ;; return the first one
ENDM
;;***********************************************************************
;; setup the data structure needed for the receiver. CX, ES:SI is the
;; len and begining of shared memory in which to set it up. It returns
;; in SI the pointer that belongs in the scb_rframes field of the system
;; control block 'len' is the length of a buffer
C507_SETUP_R_MEM_in_CX_SI_ES_out_SI_const_ES MACRO len
;; since we allocate two buffers for every frame
TOTAL_BUFF_LEN = len + (SIZE i82586_rbd) + (SIZE i82586_rfd)
mov AX, CX
sub AX, (SIZE i82586_rfd) ;; Want an extra frame desc
xor DX, DX
mov BX, TOTAL_BUFF_LEN
div BX
mov CX, AX ;; AX holds the number of buffers
mov BX, SI ;; BX start of buffer space
mov DX, len
mul DX
add SI, AX ;; SI start of buff desc
mov BP, SI
mov AX, CX
mov DX, (SIZE i82586_rbd)
mul DX
add BP, AX ;; BP points to the start of frame desc
mov AX, CX ;; CX holds the number of buffers
inc AX ;; AX holds the number of frames
C507_SETUP_R_BUFFS_in_BX_CX_SI_ES_out_SI_const_AX_BP_ES len
mov CX, AX
mov AX, SI ;; save pointer to start of buffer list
mov SI, BP
C507_SETUP_R_FRAMES_in_CX_SI_ES_const_AX_BP_ES
mov SI, BP ;; return final pointer to frames
mov ES:[SI+rfd_rbd], AX ;; link the frames to the buffs
ENDM
;;***********************************************************************
;; setup the data structures needed to communicate with the 82586.
;; SI:ES points to the shared memory window. Note that We only use
;; the memory ABOVE SI in the ES segment, thus if SI=C000 there is
;; a 4K window. In addition, some data structures are in 'standard'
;; positions that can be used directly.
;;
SCP = 0FFE0H ;; place for system control ptr
SCB = (SCP-16) ;; place for system control block
CU_GCMD = (SCB-32) ;; generic command (32 bytes long)
CU_TCMD1 = (CU_GCMD-32) ;; first trans command (32 bytes long)
CU_TCMD2 = (CU_TCMD1-32) ;; second trans command (32 bytes long)
CU_TBD1 = (CU_TCMD2-8) ;; first Transmit buffer descriptor
CU_TBD2 = (CU_TBD1-8) ;; second Transmit buffer descriptor
CU_TB1 = (CU_TBD2-1536);; first Trans buffer (1536 bytes long)
CU_TB2 = (CU_TB1-1536) ;; second Trans buffer (1536 bytes long)
START_RESERVED = CU_TB2 ;; start of fixed allocated memory
LEN_R_BUFF = 256 ;; this is a optimization param
;;*************************************************************************
;; SETUP_MEM sets up all the shared memory data structures that the 82586
;; uses SI:ES points the the begining of the memory. It is assumed that
;; the memory ranges is ES:SI to ES:FFFF
;;
C507_SETUP_MEM_in_SI_ES_const_ES MACRO
mov DI, SI ;; zero out memory, it makes reading
xor AX, AX ;; dumps easier
mov DX, SI
shr DX, 1
mov CX, 8000H
sub CX, DX
rep stosw
;; setup ROOT
mov BX, offset I82586_FIXED_ROOT
mov ES:[BX+root_bus], 0
mov ES:[BX+root_scp.offs], offset SCP
mov ES:[BX+root_scp.segm], 0
;; setup SCP
mov BX, offset SCP
mov ES:[BX+scp_busy], 1 ;; set the busy bit
mov ES:[BX+scp_scb], SCB
mov ES:[BX+scp_base.offs], 0
mov ES:[BX+scp_base.segm], 0
;; setup SCB
mov BX, offset SCB
mov ES:[BX+scb_cmd], 0
mov ES:[BX+scb_status], 0
mov ES:[BX+scb_crc_errs], 0
mov ES:[BX+scb_aln_errs], 0
mov ES:[BX+scb_rcs_errs], 0
mov ES:[BX+scb_ovrn_errs], 0
mov ES:[BX+scb_cuc], offset CU_GCMD
;; get the length of the rest of memory
mov CX, START_RESERVED
sub CX, SI
C507_SETUP_R_MEM_in_CX_SI_ES_out_SI_const_ES LEN_R_BUFF
mov BX, offset SCB
mov ES:[BX+scb_rframes], SI
;; setup transmit buffers descriptors
mov BX, CU_TCMD1
mov ES:[BX+cu_cmd], CU_CMD_EOL+CU_CMD_TRANS
mov ES:[BX+cu_params+trans_tbd], CU_TBD1
mov BX, CU_TCMD2
mov ES:[BX+cu_cmd], CU_CMD_EOL+CU_CMD_TRANS
mov ES:[BX+cu_params+trans_tbd], CU_TBD2
mov BX, CU_TBD1
mov ES:[BX+tbd_buff.offs], offset CU_TB1
mov ES:[BX+tbd_buff.segm], 0
mov BX, CU_TBD2
mov ES:[BX+tbd_buff.offs], offset CU_TB2
mov ES:[BX+tbd_buff.segm], 0
ENDM
;;***********************************************************************
;; DO_CMD simply executes the command 'command'. It assumes that all
;; parameters to the command have been set up in the DO_CMD_PARAMS
;; structure below. This command waits for completion and jumps to
;; 'fail' if unsuccessful. (note that DO_CMD_PARAMS points to the
;; command specific part of the command.
;; Note also that this command should not be used if it is possible
;; that other commands are in progress
DO_CMD_PARAMS = (CU_GCMD+6)
C507_DO_CMD_in_ES_const_BP_SI_DI_ES MACRO command, port, fail
local waitloop, done
.errb <fail>
mov BX, offset SCB
mov ES:[BX+scb_status], 0
mov ES:[BX+scb_cuc], offset CU_GCMD
mov ES:[BX+scb_cmd], SCB_CUC_START
mov BX, offset CU_GCMD
mov ES:[BX+cu_status], 0
mov ES:[BX+cu_cmd], CU_CMD_EOL+command
;; we assume that the rest of the command is set up
;; signal the 82586
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES port, C507_IO_ATTN
xor CX, CX ;; don't wait forever
waitloop:
dec CX
jz fail
mov AX, ES:[BX+cu_status]
test AX, CU_STAT_COMPLETE
jz waitloop ;; wait for completion
test AX, CU_STAT_OK
jz fail
ENDM
;;***********************************************************************
;; do the intial setup of the 3C507 card
C507_RESET_82586_in_ES MACRO port, fail
local reset_wait, reset_done, reset_loop
;; reset the 82586
mov ES:[SCP+scp_busy], 1 ;; set the busy bit
mov AL, 0
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES port, C507_IO_CTR
nop ;; wait 10 clock cycles
nop
;; just in case there was an interupt pending, clear it
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES port, C507_IO_INT_CLEAR
mov AL, C507_CTR_RUN+C507_PG_ETHER
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES port, C507_IO_CTR
nop
nop
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES port, C507_IO_ATTN
mov CX, 256
reset_loop:
cmp ES:[SCP+scp_busy], 0
jz reset_done
loop reset_loop
jmp fail
reset_done:
ENDM
;;***********************************************************************
;; do the intial setup of the 3C507 card
C507_INIT_SETUP_out_ES MACRO port, segment, len, addr, promiscuous, fail
local eaddr_loop, done, diag_fail, config_fail, setaddr_fail
local reset_fail, ru_on_fail, wait_ru_on, done_wait_ru
.errb <len>
;; adjust ES so that ES:SI point to begining and ES:FFFF the end
mov AX, segment
if ((len/16) ne 0) ;; This is a kludge because the assembler wraps 10000H
sub AX, (1000H - (len / 16))
endif
mov ES, AX
mov SI, 10000H - len
;; test the memory just a bit
mov ES:[SI], 5F75H
mov DX, ES:[SI]
mov AX, 0101H ;; signals a memory check error type 1
cmp DX, 5F75H
jnz fail
mov ES:[0FFFEH], 57A5H
mov DX, ES:[0FFFEH]
mov AX, 0102H ;; signals a memory check error type 2
cmp DX, 57A5H
jnz fail
C507_SETUP_MEM_in_SI_ES_const_ES
;; just to test the board is there, get the first char
;; of the '*3com*' identifier from the I/O ROM.
mov AL, C507_CTR_RUN+C507_PG_3COM
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES port, C507_IO_CTR
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES port, C507_IO_ADDR
mov DL, AL
mov AX, 0201H ;; signals 82586 not there
cmp DL, '*' ;; should be part of *3com*' string
jnz fail
C507_RESET_82586_in_ES port reset_fail
;; check the setup by doing a diag
C507_DO_CMD_in_ES_const_BP_SI_DI_ES CU_CMD_DIAGNOSE, port, diag_fail
;; configure ethernet params
mov BX, offset DO_CMD_PARAMS
mov word ptr ES:[BX+0], 0080CH ; fifo=8 byte count=12
mov word ptr ES:[BX+2], 02E00H ; preamble=4, add_len=6, DONT ins headers
mov word ptr ES:[BX+4], 06000H ; interframe spacing = 60h
mov word ptr ES:[BX+6], 0F200H ; retry = 15, slot time = 200h
if promiscuous eq 1
mov word ptr ES:[BX+8], 1 ; flags bit 1 means promiscuous
else
mov word ptr ES:[BX+8], 0 ; flags bit 1 means promiscuous
endif
mov word ptr ES:[BX+10], 0003CH ; minimum frame length = 60
C507_DO_CMD_in_ES_const_BP_SI_DI_ES CU_CMD_CONFIG, port, config_fail
;; set the ethernet address
mov DI, offset DO_CMD_PARAMS
mov SI, offset addr
mov CX, 3
rep movsw
C507_DO_CMD_in_ES_const_BP_SI_DI_ES CU_CMD_ADDRESS_SET, port, setaddr_fail
mov BX, offset SCB
mov ES:[BX+scb_status], 0
mov ES:[BX+scb_cmd], SCB_RUC_START
;; signal the 82586
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES port, C507_IO_ATTN
xor CX, CX
wait_ru_on:
dec CX
mov AX, ES:[BX+scb_status]
and AX, SCB_STAT_RUS_MSK
jnz done_wait_ru
dec CX
jnz wait_ru_on
done_wait_ru:
cmp AX, SCB_STAT_RUS_READY
jz done
ru_on_fail:
mov AX, 0206H ;; failed to turn RU on
jmp fail
reset_fail:
mov AX, 0202H ;; failed resetting the 82586
jmp fail
diag_fail:
mov AX, 0203H ;; signals 82586 failed noop test
jmp fail
config_fail:
mov AX, 0204H ;; signals 82586 failed config
jmp fail
setaddr_fail:
mov AX, 0205H ;; signals 82586 failed set addr
jmp fail
done:
ENDM
;;******************************************************************************
;; utility functions needed only within this module
READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES MACRO port, if_io
mov DX, if_io+port
in AL, DX ;; AL contains data read from port
ENDM
;;******************************************************************************
WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES MACRO port, if_io
mov DX, if_io+port
out DX, AL ;; AL contains data read from port
ENDM