home *** CD-ROM | disk | FTP | other *** search
- ;;******************************************************************************
- ;; wd8003.inc wd8003.inc
- ;;******************************************************************************
- ;;
- ;; Copyright (C) 1989 Northwestern University, 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.
- ;;
- ;; Northwestern University 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.
- ;;
- ;;******************************************************************************
- ;; wd8003 holds the interface routines for the western digital ethernet card
- ;; WD8003E or the starlan card WD8003S.
- ;;
- ;; The functions provided by this file are
- ;;
- ;; WD_DECLARE name, io_address, shr_seg, shr_off
- ;; WD_DEFINE name
- ;; WD_IF_R_ACCESS_out_BX_CX_ES name, no_packet
- ;; WD_IF_R_CONT_in_BX_CX_ES_const_BX_CX_DX_BP_SI_DI_ES name, ok
- ;; WD_IF_R_FREE_const_BX_CX_BP_SI_DI_ES name
- ;; WD_IF_W_ACCESS_in_CX_out_DI_ES_const_BX_CX_BP name, no_buffer
- ;; WD_IF_W_WRITE_in_CX_const_BX_BP_ES name
- ;; WD_IF_SET_ADDRESS_in_SI_const_BX_CX_BP_DI_ES name
- ;; WD_IF_COPY_in_CX_SI_DI_ES_out_SI_DI_const_BX_BP_ES name
- ;;
- ;; Variables set by this module
- ;;
- ;; wd_&name&_declared ;; one if this interface exists
- ;; if_&name&_address ;; the hardware address
- ;; if_&name&_mtu ;; the max trans unit
- ;;
- ;;******************************************************************************
-
- include wd.inc
-
- ;;******************************************************************************
- ;; data storage needed by this module
-
- wd_q_entry STRUC
- wd_q_start DW ?
- wd_q_end DW ?
- wd_q_len DW ?
- wd_q_entry ENDS
-
- wd_data STRUC
- wd_new_bndry DB 0
- wd_dequeue DB 0 ;; please dequeue the last packet
- wd_data ENDS
-
-
- ;;******************************************************************************
- ;; IF_DECLARE name, io_address, shr_seg, shr_off, wbuffs
- ;; declares an interface object. 'io_address' is the address of the
- ;; start of the 8003E control registers. 'shr_seg' and
- ;; 'shr_off' is the address of the WD8003 card buffer
- ;; This address must be a multiple of 512. 'timer' is a timer
- ;; object. 'wbuffs' is the number of 256byte blocks to allocate to
- ;; writing (should be 6 for ethernet, more like 16 for starlan)
- ;;
- WD_DECLARE MACRO name, io_address, shr_seg, shr_off, timer, wbuffs
- .errb <name>
- .errb <io_address>
- .errb <shr_seg>
- .errb <shr_off>
-
- .DATA
- wd_&name&_declared = 1
- wd_&name&_io = io_address ;; set compile time values
- wd_&name&_shared_off = shr_off
- wd_&name&_shared_seg = shr_seg
-
- wd_&name&_timer = timer
- wd_&name&_buff = name*100+1 ;; make up names in my space
- wd_&name&_wqueue = name*100+2
- ifb <wbuffs>
- wd_&name&_wbuffs = 6
- else
- wd_&name&_wbuffs = wbuffs
- endif
-
- if_&name&_mtu = 1514
- global wd_&name&_data:wd_data
- global if_&name&_address:word
-
- .CODE
- global wd_&name&_dequeue:near
- global wd_&name&_real_define:near
-
- BUFF_DECLARE %wd_&name&_buff, %(wbuffs*256), shr_off
- QUEUE_DECLARE %wd_&name&_wqueue, wbuffs, %(size wd_q_entry)
- ENDM
-
-
- ;;******************************************************************************
- ;; IF_DEFINE name
- ;; sets asside memory an interface object and initializes it. This
- ;; routine is a no-op if 'name' was not declared
- ;;
- WD_DEFINE MACRO name
-
- call wd_&name&_real_define
- ENDM
-
- WD_REAL_DEFINE MACRO name
- LOCAL loop1, loop2, loop3, around
- .errb <name>
-
- ifdef wd_&name&_declared
- .DATA
- if_&name&_address DW 3 DUP (0)
- wd_&name&_data wd_data <> ;; create storage needed
-
- .CODE
- jmp around
- wd_&name&_dequeue:
- WD_IF_W_DEQUEUE_const_BX_BP_ES name
- TIMER_RETURN %wd_&name&_timer
- around:
- wd_&name&_real_define:
- BUFF_DEFINE %wd_&name&_buff ;; initialize the buff manager
-
- QUEUE_DEFINE %wd_&name&_wqueue ;; and write queue
-
- mov cx, 6 ;; get the ethernet address
- mov bx, OFFSET if_&name&_address
- mov dx, wd_&name&_io+ADDROM ;; point to the begining of the io space
- loop1: ;; which is the reg holding the Eth addr
- in AL, DX
- mov [BX], AL
- inc DX
- inc BX
- loop loop1
-
- mov AL, 80h ;; reset the card
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES 0, wd_&name&_io
- mov AL, 00h
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES 0, wd_&name&_io
-
- ;; register 0 is the MSR (Memory base reg)
- mov AL, (wd_&name&_shared_seg+(wd_&name&_shared_off/16))/512
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES 0, wd_&name&_io
-
- mov AL, MSK_PG0 + MSK_RD2 ;; make sure we are on page 0
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
- mov AL, MSK_BMS + MSK_FT10 ;; select FIFO threshold = 8 bytes
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES DCR, wd_&name&_io
- xor AL, AL ;; clear RBCR0,1
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES RBCR0, wd_&name&_io
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES RBCR1, wd_&name&_io
- mov AL, MSK_MON ;; turn off receiving
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES RCVR, wd_&name&_io
- xor AL, AL ;; clear TCR
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES TCR, wd_&name&_io
-
- mov AL, STOP_PG ;; end of input buffer (in 256b pages)
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES PSTOP, wd_&name&_io
- mov AL, wd_&name&_wbuffs ;; start of input buffer (in 256b pages)
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES PSTART, wd_&name&_io
- ;; Boundry of read in from not read in yet
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES BNRY, wd_&name&_io
- mov AL, -1
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES ISR, wd_&name&_io
- mov AL, 0 ;; no interupts
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES IMR, wd_&name&_io
-
- mov AL, MSK_PG1 + MSK_RD2 ;; make sure we are on page 1
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
- mov AL, wd_&name&_wbuffs+1 ;; Set input pointer for queue
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CURR, wd_&name&_io
-
- mov CX, 6 ;; set the ethernet address
- mov BX, OFFSET if_&name&_address
- mov DX, wd_&name&_io+PAR0
- loop2:
- mov AL, [BX] ;; get 1 byte into AL
- out DX, AL ;; write to PAR
- inc BX
- inc DX
- loop loop2
-
- mov CX, 8 ;; set the multicast address to all 0's
- mov DX, wd_&name&_io+MAR0
- xor AL, AL
- loop3:
- out DX, AL
- inc DX
- loop loop3
-
- mov AL, MSK_PG0 + MSK_RD2 ;; make sure we are on page 0
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
- mov AL, MSK_STA + MSK_RD2 ;; activate the 8390
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
-
- mov AL, MSK_AB ;; turn on receiver
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES RCVR, wd_&name&_io
-
- mov byte ptr wd_&name&_data.wd_dequeue, 0 ;; no packet to dequeue
- 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
- ;;
- WD_IF_R_ACCESS_out_BX_CX_ES MACRO name, no_packet
- local inside, good_packet, wrapped, new_wrapped, bad_packet, ok_status
- local good_length, truncate
- .errb <no_packet>
-
- mov AL, MSK_PG0 + MSK_RD2 ;; read the BNRY register into AL
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES BNRY, wd_&name&_io
-
- inc AL ;; increment with wrap around
- cmp AL, STOP_PG
- jb inside
- mov AL, wd_&name&_wbuffs
- inside:
- mov BH, AL ;; save it in BH
-
- mov AL, MSK_PG1 + MSK_RD2 ;; read CURR register into AL
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES CURR, wd_&name&_io
-
- cmp AL, BH
- je no_packet
- xor BL, BL ;; BX now holds pointer to packet
-
- mov DX, wd_&name&_shared_seg ;; ES = segment address
- mov ES, DX
- mov AH, ES:[BX+wd_&name&_shared_off] ;; get the status
- cmp AH, SMK_PRX ;; is it good
- jz ok_status
- cmp AH, SMK_PRX+SMK_PHY
- jnz bad_packet
-
- ok_status:
- mov AH, ES:[BX+1+wd_&name&_shared_off] ;; pointer to the next packet
- ;; sanity check on next packet pointer AH
- cmp BH, AL ;; is BNDRY+1 <= CURR?
- ja wrapped
- cmp AH, BH
- jb bad_packet
- cmp AH, AL
- jbe good_packet
- jmp bad_packet
- wrapped:
- cmp AH, BH
- jb new_wrapped
- cmp AH, STOP_PG
- jnb bad_packet
- jmp good_packet
- new_wrapped:
- cmp AH, wd_&name&_wbuffs
- jb bad_packet
- cmp AH, AL
- jbe good_packet
- bad_packet:
- ;; set BNDRY = BNDRY+1 and try again
- mov AL, MSK_PG0 + MSK_RD2 ;; make sure we are on page 0
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
-
- mov AL, BH ;; write the new BNRY register
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES BNRY, wd_&name&_io
- jmp no_packet ;; return bad status
- good_packet:
-
- mov wd_&name&_data.wd_new_bndry, AH ;; save it for R_FREE
- mov CX, ES:[BX+wd_&name&_shared_off+2] ;; load the length
- add BX, wd_&name&_shared_off+4 ;; BX point to begining of the packet
- sub CX, 4
-
- cmp CX, 1514 ;; sanity check
- jle good_length
- cmp CH, CL
- jne truncate
- xor CH, CH ;; fix western digital bug
- jmp good_length
- truncate:
- mov CX, 1514
- good_length:
- 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.
- ;;
- WD_IF_R_FREE_const_BX_CX_BP_SI_DI_ES MACRO name
- local inside
- .errb <name>
-
- mov AL, MSK_PG0 + MSK_RD2 ;; make sure we are on page 0
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
-
- mov AL, wd_&name&_data.wd_new_bndry ;; Retreive NEW_BOUNDRY
- dec AL
- cmp AL, wd_&name&_wbuffs
- jge inside
- mov AL, STOP_PG-1
- inside: ;; write the new BNRY register
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES BNRY, wd_&name&_io
- ENDM
-
-
- ;;******************************************************************************
- ;; WD_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
- ;;
- WD_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, OFFSET wd_&name&_shared_off+STOP_PG*256
- 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
- ;;
- WD_IF_W_ACCESS_in_CX_out_DI_ES_const_BX_CX_BP MACRO name, no_buffer
- .errb <no_buffer>
-
- BUFF_CHECK_in_CX_out_SI_DI_const_BX_CX_DX_BP_ES %wd_&name&_buff, no_buffer
- mov DI, SI
- mov SI, offset wd_&name&_shared_seg
- mov ES, SI
- 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.
- ;;
- WD_IF_W_WRITE_in_CX_const_BX_BP_ES MACRO name
- local done
- .errb <name>
-
- ;; queue up the packet
- mov DX, CX ;; save CX
- add CX, 255 ;; round up to a multiple of 256
- xor CL, CL
- BUFF_CHECK_in_CX_out_SI_DI_const_BX_CX_DX_BP_ES %wd_&name&_buff, done
- mov CX, DI ;; save buff_end
- QUEUE_ENQUEUE_out_DI_const_BX_CX_DX_BP_SI_ES %wd_&name&_wqueue, done
- xchg CX, DI ;; restore DI, save queue entry
- BUFF_GET_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES %wd_&name&_buff
- xchg DI, CX ;; restore queue entry, save_end
- mov [DI+wd_q_start], SI
- mov [DI+wd_q_end], CX
- mov [DI+wd_q_len], DX
- WD_IF_W_DEQUEUE_const_BX_BP_ES name, 1
- done:
- ENDM
-
- ;;******************************************************************************
- ;; WD_IF_W_DEQUEUE tryes to empty the write queue. It checks if there is
- ;; something in the queue, and if there is, it tries to send it. If the
- ;; queue is not empty when it leaves, it submits itself to go off again so
- ;; that it can try again. 'write' is true (not_blank) if this is called
- ;; from W_WRITE
- ;;
- WD_IF_W_DEQUEUE_const_BX_BP_ES MACRO name, write
- local done, requeue, not_dequeueing, no_more
- .errb <name>
-
- mov CL, wd_&name&_data.wd_dequeue ;; save the old value
- READ_PORT_out_AL_const_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
- test AL, MSK_TXP
- ifnb <write>
- jnz done
- else
- jnz requeue
- endif
- cmp CL, 0 ;; dequeue the next packet ?
- jz not_dequeueing
- QUEUE_HEAD_out_SI_const_AX_BX_CX_DX_BP_DI_ES %wd_&name&_wqueue,no_more
- mov DI, word ptr [SI+wd_q_end]
- BUFF_FREE_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES %wd_&name&_buff
- QUEUE_DEQUEUE_in_SI_const_AX_BX_CX_DX_BP_DI_ES %wd_&name&_wqueue
- not_dequeueing:
-
- ;; is there another to send?
- QUEUE_HEAD_out_SI_const_AX_BX_CX_DX_BP_DI_ES %wd_&name&_wqueue, no_more
-
- ;; send the packet
- mov AL, MSK_PG0 + MSK_RD2 ;; make sure we are in register page 0
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
- mov AX, [SI+wd_q_len]
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES TBCR0, wd_&name&_io
- mov AL, AH
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES TBCR1, wd_&name&_io
-
- mov AL, byte ptr [SI+wd_q_start+1] ;; where the packet starts
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES TPSR, wd_&name&_io
- mov AL, MSK_TXP + MSK_RD2 ;; send the packet
- WRITE_PORT_in_AL_const_AX_BX_CX_BP_SI_DI_ES CMDR, wd_&name&_io
- mov wd_&name&_data.wd_dequeue, 1
-
- ifnb <write>
- or CL, CL
- jnz done
- endif
- requeue:
- ;; submit it to the timer queue
- xor AX, AX
- mov CX, BX ;; save BX
- TIMER_MARK_in_AX_const_CX_BP_ES %wd_&name&_timer, wd_&name&_dequeue
- mov BX, CX ;; restore BX
- jmp done
- no_more:
- mov wd_&name&_data.wd_dequeue, 0
- done:
- 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)
- ;;
-
- WD_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 shouldn't care if the
- ;; input buffer is contiguous) COPY updates the pointers SI and DI
- ;; to the end of the packet, and COPY could be called again if CX is not
- ;; the total packet length (Note that CX MUST be even if you care about
- ;; SI, and DI being updated properly)
- ;;
- WD_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, wd_&name&_shared_seg
- mov DS, AX
-
- mov AX, OFFSET wd_&name&_shared_off+STOP_PG*256
- sub AX, SI ;; AX holds length to wrap line
- CMP AX, CX
- jl wrap ;; wrap if AX less than packet lenght
- inc CX ;; AX >= CX, no wrap is necessary
- shr CX,1
- rep movsw
- jmp done
- wrap:
- XCHG AX, CX ;; length is now length to wrap line
- SUB AX, CX ;; AX holds remainder
- inc CX
- shr CX,1
- rep movsw
- mov SI, OFFSET wd_&name&_shared_off+wd_&name&_wbuffs*256
- mov CX, AX
- inc CX
- shr CX,1
- rep movsw
- done:
- mov DS, DX ;; restore DS
- 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
-
-