Metropoli BBS
VIEWER: net503.asm MODE: TEXT (ASCII)
;  3C503 Driver
;
;	Kurt Mahan ( based on WD8003 by Tim Krauskopf )
;
;****************************************************************************
;*                                                                          *
;*                                                                          *
;*      part of NCSA Telnet                                                 *
;*      by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer     *
;*		neat other things by Kurt Mahan
;*                                                                          *
;*      National Center for Supercomputing Applications                     *
;*      152 Computing Applications Building                                 *
;*      605 E. Springfield Ave.                                             *
;*      Champaign, IL  61820                                                *
;*                                                                          *
;*                                                                          *
;****************************************************************************
;

	TITLE	NETSUPPORT -- LOW LEVEL DRIVERS FOR ETHERNET
;
;  Assembler support for interrupt-driven Ethernet I/O on the PC
;
;  Reads and writes from the 8K buffer on the WD card.
;  Started 4/11/88
;
;Microsoft EQU 1
;Lattice EQU 1
ifndef Microsoft
    ifndef Lattice
        if2
            %out
            %out ERROR: You have to specify "/DMicrosoft" OR "/DLattice" on the
            %out        MASM command line to determine the type of assembly.
            %out
        endif
        end
    endif
endif

ifdef Microsoft
X	EQU	6
	DOSSEG
	.MODEL	LARGE
else
    	NAME	NET503
  	INCLUDE	DOS.MAC
  	SETX
endif

;
;	EQUATES for the 3C503 board
;

CMDR	equ	00h			  ; ports on the 8390 ( page 0 )
PSTART	equ	01h
PSTOP	equ	02h
BNRY	equ	03h
TPSR	equ	04h
TBCR0	equ	05h
TBCR1	equ	06h
ISR	equ	07h
RSAR0	equ	08h
RSAR1	equ	09h
RBCR0	equ	0ah
RBCR1	equ	0bh
XRCR	equ	0ch
TCR	equ	0dh
DCR	equ	0eh
IMR	equ	0fh

PAR0	equ	01h			  ; ports on the 8390 ( page 1 )
PAR1	equ	02h
PAR2	equ	03h
PAR3	equ	04h
PAR4	equ	05h
PAR5	equ	06h
CURR	equ	07h
MAR0	equ	08h
MAR1	equ	09h
MAR2	equ	0ah
MAR3	equ	0bh
MAR4	equ	0ch
MAR5	equ	0dh
MAR6	equ	0eh
MAR7	equ	0fh

PSTR	equ	400h			  ; ports on the gate array
PSPR	equ	401h
DQTR	equ	402h
BCFR	equ	403h
PCFR	equ	404h
GACFR	equ	405h
CTRL	equ	406h
STREG	equ	407h
IDCFR	equ	408h
DAMSB	equ	409h
DALSM	equ	40ah
VPTR2	equ	40bh
VPTR1	equ	40ch
VPTR0	equ	40dh
RFMSB	equ	40eh
RFLSB	equ	40fh

STRT_PG	equ	26h			  ; start at page 6
STOP_PG	equ	40h			  ; end at page 31
TSTRT_PG equ	20h			  ; transmit start page

;
;  macros for writing to NET503 board
;
;***********************************************************************
;
;	Macros, from example driver
;
;***********************************************************************

;
; MACRO rd_wd8
;   Reads port specified on macro call. Leaves byte in AL.
;

rd_wd8	MACRO	port
	push	dx
	mov	DX, WDBASE
	add	DX, port		; DX contains address of port
	in	AL, DX			; AL contains data read from port
	pop	dx
	ENDM

;
; MACRO wr_wd8
;   Writes byte in AL to port specified on macro call.
;

wr_wd8	MACRO	port
	push	dx
	mov	DX, WDBASE
	add	DX, port		; DX contains address of port
	out	DX, AL			; AL contains data to be written to port
	pop	dx
	ENDM

;
ifdef Microsoft
;DGROUP	group	_DATA
;_DATA	segment	public 'DATA'
;	assume	DS:DGROUP
	.data
else
	DSEG
endif
;	PUBLIC	STAT,BUFPT,BUFORG,BUFEND,BUFREAD,BUFBIG,BUFLIM,OFFS
;
;  The pointers below are actually DWORDs but we access them two
;  bytes at a time.
;
; STAT change to RSTAT because of name clash with MSC library routine
ifdef Microsoft
	EXTRN	_RSTAT:BYTE	; last status from read
	EXTRN	_BUFPT:WORD	; current buffer pointer
	EXTRN	_BUFORG:WORD	; pointer to beginning of buffer
	EXTRN	_BUFEND:WORD	; pointer to end of buffer
	EXTRN	_BUFREAD:WORD	; pointer to where program is reading
	EXTRN	_BUFBIG:WORD	; integer, how many bytes we have
	EXTRN	_BUFLIM:WORD	; integer, max bytes we can have
else
	EXTRN	RSTAT:BYTE	; last status from read
	EXTRN	BUFPT:WORD	; current buffer pointer
	EXTRN	BUFORG:WORD	; pointer to beginning of buffer
	EXTRN	BUFEND:WORD	; pointer to end of buffer
	EXTRN	BUFREAD:WORD	; pointer to where program is reading
	EXTRN	BUFBIG:WORD	; integer, how many bytes we have
	EXTRN	BUFLIM:WORD	; integer, max bytes we can have
endif

WDBASE	DW	00h		; base ioaddr
WDADD	DW	00h		; base shared mem addr
DEAF	DB	00H		; when we can't handle any more packets
OFFS	DW	00H		; how many times the handler was turned off
WIRE	DB	02h		; thin as the default
;
ifdef Microsoft
;_DATA	ends
else
	ENDDS
endif
;
;   The subroutines to call from C
;
ifdef Microsoft
;_TEXT	segment	public	'CODE'
;	assume CS:_TEXT
	.code
	PUBLIC	_E4RECV,_E4ETOPEN,_E4ETCLOSE,_E4GETADDR
ifdef NOT_USED
    PUBLIC  _E4SETADDR
endif
    PUBLIC  _E4XMIT,_E4ETUPDATE
	PUBLIC _E4SETWIRE
else
	PSEG
	PUBLIC	E4RECV,E4ETOPEN,E4ETCLOSE,E4GETADDR
ifdef NOT_USED
    PUBLIC  E4SETADDR
endif
    PUBLIC  E4XMIT,E4ETUPDATE, E4SETWIRE
endif


;*****************************************************************
;
; E4SETWIRE
;
; WHAT TYPE OF WIRE ARE WE USING?  THIN OR THICK
;
; USAGE: E4SETWIRE(INT WIRE)
;

ifdef Microsoft
_E4SETWIRE	PROC FAR
else
E4SETWIRE	PROC FAR
endif

	PUSH BP
	MOV  BP,SP
	push ds
	mov  ax,seg WIRE
	mov  ds,ax
	XOR  AX,AX
	MOV  AL,[BP+X]		; GET WIRE TYPE
	MOV  WIRE,AL 		; STORE IT!
	pop  ds
	POP  BP
	RET

ifdef	Microsoft
_E4SETWIRE	ENDP
else
E4SETWIRE	ENDP
endif


;******************************************************************
;  ETOPEN
;     Initialize the Ethernet board, set receive type.
;
;  usage:  etopen(s,irq,addr,ioaddr)
;           char s[6];       ethernet address
;           int irq,addr,ioaddr;     
;                interrupt number (unused), base mem address and
;                i/o address to use
;
ifdef	Microsoft
_E4ETOPEN	PROC	FAR
else
E4ETOPEN	PROC	FAR
endif
;
;	init addresses and things
;

	PUSH	BP
	MOV	BP,SP

	mov	AX,[BP+X+8]		; install ioaddr
	mov	WDBASE,AX
	mov	AX,[BP+X+6]		; install shared mem addr
	mov	WDADD,AX
;
;	initialize the gate array
;
	push	ds
	mov	ax,seg WIRE
	mov	ds,ax
	mov	al,1			; reset the board -- onboard xcvr
	or 	al,WIRE
	wr_wd8	CTRL
	mov	al,0			; done with reset -- still onboard xcvr
	or	al,WIRE
	wr_wd8	CTRL
	wr_wd8	CTRL
	pop	ds
;
;	set the vector pointers ( VPTR0 - VPTR2 ) so that the shared memory
;	won't go byebye on us accidentally ( point to ffff00h )
;
	mov	al,0ffh
	wr_wd8	VPTR2
	wr_wd8	VPTR1
	xor	al,al
	wr_wd8	VPTR0
;
;	lookup the shared memory address ( such a neat card, huh -- we can
;	actually read it up from the gate array )
;
;	PCFR : high bits = ( 7 .. 0 )
;				bit 7 = dc00
;				bit 6 = d800
;				bit 5 = cc00
;				bit 4 = c800
;
;
	rd_wd8	PCFR			; read prom config register
	test	al,80h			; dc00?
	jz	eto100
	mov	WDADD,0da00h
    jmp SHORT etok
eto100:	test	al,40h			; d800?
	jz	eto200
	mov	WDADD,0d600h
    jmp SHORT etok
eto200:	test	al,20h			; cc00?
	jz	eto300
	mov	WDADD,0ca00h
    jmp SHORT etok
eto300:	test	al,10h			; c800?
	jz	eto400
	mov	WDADD,0c600h
    jmp SHORT etok
;
eto400:	mov	ax,-1			; mem not enabled
	pop	bp
	ret
;
;	ok, WDBASE is setup as the base memory address
;
etok :	mov	al,0c9h			; dis ints, ram sel, bank 0
	wr_wd8	GACFR
;
;	setup the start/stop pages 
;
	mov	al,STRT_PG		; starting page
	wr_wd8	PSTR
	mov	al,STOP_PG		; ending page
	wr_wd8	PSPR
;
;	setup the interrupt and dma request stuff
;
	xor	al,10h			; no ints, no dma channels
	wr_wd8	IDCFR			; interrupt/dma channel ctrl
;
;	set DRQ timer to 8
;
	mov	al,8
	wr_wd8	DQTR
;
;	set dma msb to 20h
;
	mov	al,TSTRT_PG		; transmit start page
	wr_wd8	DAMSB
	xor	al,al			; 0
	wr_wd8	DALSM
;
	cld
;
; initialize the LAN Controller register
;					
	push	ds
	mov	ax,seg WIRE
	mov	ds,ax
	mov	al,0
	or	al,WIRE
	wr_wd8	CTRL			; make sure its switched in
	pop	ds
;
; program for page 0
;
	mov	AL, 21h			  ; page 0, abort any pending dma
	wr_wd8	CMDR
;
;	clear interrupt regs
;
	mov	al, 0ffh
	wr_wd8	ISR
;
; initial DCR data configuration
;
	mov	AL, 48h			   ; burst dma, fifo thresh = 8 bytes
	wr_wd8	DCR
;
; initial TCR
;
	xor	AL, AL			
	wr_wd8	TCR			; normal operation
;
; initial RCR to monitor mode
;
	mov	AL, 20h				; monitor mode
	wr_wd8	XRCR
;
; set page start/page stop/ boundary
;
	mov	AL,STRT_PG
	wr_wd8	PSTART
	mov	al,STOP_PG
	wr_wd8	PSTOP
	mov	al,STRT_PG		; go with the WD style of things
	wr_wd8	BNRY
;
; program for page 1
;
	mov	AL, 60h	 		  ; page 1, abort any pending dma
	wr_wd8	CMDR
;
; initial physical addr
;
	mov	DX, WDBASE		; get board io base
	push	DS
	mov	ax,[bp+X+2]		; get seg from parms
	mov	ds,ax

	mov	CX, 6			; should be 6 for Ethernet
	mov	BX, [BP+X]		; ptr to adr in BX
	add	DX, PAR0		; i/o address of PAR0 in DX
lopa:
	mov	AL, [BX]		; get 1 byte into AL
	out	DX, AL			; write to PAR
	inc	BX
	inc	DX
	loop	lopa
	pop	DS
;
; initial multicast filter,  write all 0's  into MAR0 - MAR7
;
	mov	CX, 8
	mov	DX, WDBASE
	add	DX, MAR0		; i/o address of MAR0 in DX
	xor	AL, AL			
lopb:
	out	DX, AL
	inc	DX
	loop	lopb					
;
;	set CURR page
;
	mov	al,STRT_PG+1
	wr_wd8	CURR
;
; program for page 0
;
	mov	al,21h			; page 0, stop any dma stuff
	wr_wd8	CMDR
;
; initial IMR
;
	xor	al,al			; ***NCSA Telnet does not need interrupts
	wr_wd8	IMR			; enable interrupt
	mov	al,0ffh
	wr_wd8	ISR
;
;	clear byte counts
;
	xor	AL, AL
	wr_wd8	RBCR0
	wr_wd8	RBCR1
;	
; put 8390 on line
;
	mov	al,22h			; start up the 8390
	wr_wd8	CMDR
;
; program RCR to normal operation (MSK_AB, no MSK_AM)
;
	mov	al,4				; accept broadcast packets
	wr_wd8	XRCR
;
	mov	al,0c9h
	wr_wd8	GACFR
;
;	I'm happy to keep GACFR at c9 ( no interrupts )
;
;	return NO ERROR
;
	XOR	AX,AX
	POP	BP
	RET
;
ifdef	Microsoft
_E4ETOPEN	ENDP
else
E4ETOPEN	ENDP
endif

ifdef NOT_USED
;
;******************************************************************
;  SETADDR
;    set the Ethernet address on the board to 6 byte ID code
;
;   usage:   setaddr(s,basea,ioa);
;             char s[6];           ethernet address to use
;             int basea;           shared memory base address 
;             int ioa;             io address for board
;
ifdef	Microsoft
_E4SETADDR	PROC	FAR
else
E4SETADDR	PROC	FAR
endif

	PUSH	BP
	MOV	BP,SP
;
;  not used for this board, set during etopen
;
	POP	BP
	RET
ifdef	Microsoft
_E4SETADDR	ENDP
else
E4SETADDR	ENDP
endif
endif       ; NOT_USED
;
;*******************************************************************
;  GETADDR
;     get the Ethernet address off of the board
;
;   usage:  getaddr(s,address,ioaddr);
;	char s[6];           will get six bytes from the PROM
;       int address;
;       int ioaddr;      mem address and ioaddress to use
;
ifdef	Microsoft
_E4GETADDR	PROC	FAR
else
E4GETADDR	PROC	FAR
endif

	PUSH	BP
	MOV	BP,SP
	PUSH	DS
	MOV	AX,[BP+X+2]	; SEG of where to put info
	MOV	DS,AX
	MOV	BX,[BP+X]	; address of where to put info
	mov	cx,6
	mov	dx,[BP+X+6]	; ioaddr for board
;
;	set the gate array to look at the lower 16 bytes of the prom
;
	push	ds
	mov	ax,seg WIRE
	mov	ds,ax
	mov	al,1
	or	al,WIRE
	pop	ds
	push	dx
	add	dx,CTRL
	out	dx,al
	mov	al,2
	out	dx,al
	mov	al,6
	out	dx,al
	
	pop	dx
	push	dx
;
getloop:
	in	al,dx
	mov	[bx],al		; store where we want
	inc	dx
	inc	bx
	loop	getloop
;
;	reset the gate array to normal mapping
;
	pop	dx
	push	ds
	mov	ax,seg WIRE
	mov	ds,ax
	mov	al,0		; still onboard xcvr 
	or	al,WIRE
	pop	ds
	add	dx,CTRL
	out	dx,al
	POP	DS
	POP	BP		
	RET
ifdef	Microsoft
_E4GETADDR	ENDP
else
E4GETADDR	ENDP
endif

;
;***********************************************************************
;  ETCLOSE
;        shut it down, remove the interrupt handler
;
;  usage:  etclose();
;
;
ifdef	Microsoft
_E4ETCLOSE	PROC	FAR
else
E4ETCLOSE	PROC	FAR
endif

	RET
ifdef	Microsoft
_E4ETCLOSE	ENDP
else
E4ETCLOSE	ENDP
endif
;
;************************************************************************
;   Receive
;   This is a CPU hook for boards that must be polled before we can
;   deliver packets into the receive buffer.  (i.e. no interrupts used)
;
;    usage:  recv();
;
ifdef	Microsoft
_E4RECV	PROC	FAR
else
E4RECV	PROC	FAR
endif

	push	bp
	push	es
;
;  check for data which can be read
;
	rd_wd8	XRCR			; read the Rx Status Register
	and	al,01h			; check for a packet
	jnz	snore			; none -- go onto something else
	jmp	end_rx
snore:
;
;	ok, the chip thinks we have a packet to read
;
	mov	AL, 61h			; page 1
	wr_wd8	CMDR
	rd_wd8	CURR
	mov	BL, AL			; CURR in BL 
;
	mov	AL, 20h			; page 0
	wr_wd8  CMDR
	rd_wd8	BNRY			; BNRY in AL
;
	add	AL, 1			; start page of frm in AL
	cmp	AL, STOP_PG		; check boundary
	jne	go_cmp
	mov	AL, STRT_PG		
go_cmp:
	cmp	AL, BL			
	jne	gotone
	jmp 	end_rx			; buff ring empty
gotone:
;
; ring not empty
;
	mov	BH, AL
	xor	BL, BL			; BX has the rx_frm pointer
	push	BX			; save the frm ptr
        mov	AX, WDADD		; shared mem base
	mov 	ES, AX			; ES has the shr seg paragraph
	mov	AL, ES:[BX]		; AL has the status byte
;	test	AL, SMK_PRX		; if rx good
	test	al,1
	jnz	readit
	jmp	fd_bnry			; rx error, drop frm by forward bnry
readit:
;
;  set up to read the next packet from the net
;
;
;  get ready for next packet
;
	cld			; moves in fwd dir
;
;  check for buffer overrun or catching up with reader
;
;  implicit 64K max buffer, should stop before 64K anyway
;
ifdef	Microsoft
	MOV	AX,_BUFBIG	; how much stuff is in buffer
	MOV	BX,_BUFLIM	; what is our size limit?
else
	MOV	AX,BUFBIG	; how much stuff is in buffer
	MOV	BX,BUFLIM	; what is our size limit?
endif
	CMP	AX,BX
	JNA	ISROOM		; we are ok
;
;  no room at the Inn. 
;
    JMP SHORT fd_bnry     ; can't do much, we lose packets until restarted

;
;  wrap pointer around at end, we know that we have room
;
ISROOM:
ifdef	Microsoft
	MOV	DI,word ptr [_BUFPT]     ; where buffer is
	MOV	DX,word ptr [_BUFEND]	; right before 2K safety area
else
	MOV	DI,word ptr [BUFPT]     ; where buffer is
	MOV	DX,word ptr [BUFEND]	; right before 2K safety area
endif

	CMP	DX,DI			; see if pointer is over limit
	JA	OKAYREAD		; we are not at wrap-around

ifdef	Microsoft
	MOV	AX,word ptr [_BUFORG]	; wrap to here
	MOV	word ptr [_BUFPT],AX	; wrap-around
else
	MOV	AX,word ptr [BUFORG]	; wrap to here
	MOV	word ptr [BUFPT],AX	; wrap-around
endif
	MOV	DI,AX			; di also

OKAYREAD:
;
;
;  start the copy of the new packet
;  pointer to the shared memory offset is in BX
;  At this offset, you will find:
;    1 byte - read status, usually 21h
;    1 byte - pointer, page # of next packet
;    2 bytes - length of data in packet, swapped for you already
;    n bytes - that many bytes of Ethernet packet spread
;       over n div 256 pages (allowing four lost bytes in first packet)
;
;
	pop	si		; get packet pointer back into si
	push	si		; restore for fd_bnry to read
;
;  save regs while moving packet to buffer
;  set up ds for buffer, even though we switch it later
;
	push	es
	push	ds
ifdef	Microsoft
	MOV	AX,word ptr [_BUFPT+2]	; buffer's ds
else
	MOV	AX,word ptr [BUFPT+2]	; buffer's ds
endif
	mov	ds,ax
;
;  here, DS:DI contains where we want to put the packet.
;
newpkt:
	add	si,2		; offset for length field
	mov	dx,es:[si]	; value of length of recd packet

	mov	[di],dx		; put the accumulated size there
	inc	si
	inc	si
	inc	di
	inc	di		; now it is the data pointer
;
;
;  Actually move the data
;    DX has packet size in bytes
;    ES:SI has the source pointer  } need to switch
;    DS:DI has the dest pointer    } es and ds
;    Remember, 256 byte pages wrap around at STOP_PG and there
;    are max 252 bytes in the first page
;
	mov	cx,dx
	cmp	cx,252
	jng	shrt
	mov	cx,252		; first page len
shrt:
	mov	ax,ds
	mov	bx,es
	mov	ds,bx
	mov	es,ax		; swap them

	mov	bx,dx		; save a copy of data length

mvpg:				; start of page move loop
	sub	dx,cx
	shr	cx,1		; convert to words
	jnc	iseven
	movsb			; move odd one if needed
iseven:
	rep	movsw		; move all words in one page

	cmp	dx,0		; how many left to move?
	jng	donepg
	mov	cx,dx
	cmp	cx,256
	jng	shrtr
	mov	cx,256		; one more page
shrtr:
	mov	ax,si		; look at source page
	cmp	ah,STOP_PG
	jl	mvpg
	mov	ah,STRT_PG	; wrap around at this page boundary
	mov	si,ax		; put back in si for rest of packet
	jmp	mvpg

donepg:

	pop	ds
	pop	es		; put regs back so ES is shared mem

;
; update the pointer and length in the buffer
;  DI already points just past end of data just placed there
;
ifdef	Microsoft
	MOV	word ptr [_BUFPT],di	; it is here, now
	MOV	AX,word ptr [_BUFBIG]	; total amount of stuff in buffer
else
	MOV	word ptr [BUFPT],di	; it is here, now
	MOV	AX,word ptr [BUFBIG]	; total amount of stuff in buffer
endif
	ADD	AX,BX		; add in size of this packet
	INC	AX
	INC	AX		; to cover the length value
ifdef	Microsoft
	MOV	word ptr [_BUFBIG],AX	; after adding in current packet size
else
	MOV	word ptr [BUFBIG],AX	; after adding in current packet size
endif
;
;
;  signs that something is actually happening
;
;	push	es
;	MOV	AX,0B000H       ; screen
;	MOV	ES,AX
;	MOV	DI,3998		; lower right corner
;	INC	cs:ICNT
;	MOV	al,cs:ICNT	; character
;	STOSB
;	pop	es
;


; drop bad frame by forwarding the BNRY register
;  or just normal BNRY update after frame read
;
fd_bnry:				; drop frm by forward BNRY
	pop	BX			; restore frm ptr in BX
	add	BX, 1
	mov	AL, ES:[BX]		; next frm start page in AL
	sub	AL, 1			; new BNRY in AL
	cmp	AL, STRT_PG		; check boundary
	jge	wrbnry
	mov	AL, STOP_PG - 1
wrbnry:
	wr_wd8	BNRY

end_rx:
	pop	es
	POP	BP

	RET			; for compatibility with other drivers
ICNT	db	0
ifdef	Microsoft
_E4RECV	ENDP
else
E4RECV	ENDP
endif

;
;************************************************************************
;  XMIT         
;     send a packet to Ethernet
;     Is not interrupt driven, just call it when you need it.
;
;  usage:   xmit(packet,count)
;		char *packet;
;		int count;
;
;   Takes a packet raw, Ethernet packets start with destination address,
;   and puts it out onto the wire.  Count is the length of packet < 2048
;
;   checks for packets under the Ethernet size limit of 60 and handles them
;
ifdef	Microsoft
_E4XMIT	PROC	FAR
else
E4XMIT	PROC	FAR
endif

	PUSH	BP
	MOV	BP,SP
	push	es
	PUSH	DS		; set up proper ds for the buffer
;
;	ok, let's hangout here waiting for the transmitter
;
	mov	bx,8000h	; adequate timeout
twait:	rd_wd8	CMDR		; read up the command reg
	and	al,04h		; xmit flag
	jz    	tfree
	dec	bx
	jnz	twait		; keep going till timeout
	mov	ax,-1		; bummer, it hates me
	pop	ds
	pop	es
	pop	bp
	ret
tfree:
	cld
;
	mov	al,0c9h
	wr_wd8	GACFR
;
	mov	ax,WDADD	; shared memory address in ax
	mov	es,ax		; use es for this
;
;  move packet into position, set up regs
;
	MOV	AX,[BP+X+2]	; get data ds
	MOV	DS,AX
	MOV	SI,[BP+X]	; DS:SI points to data buffer

	MOV    	CX,[BP+X+4]	; count of bytes
	CMP	CX,60		; minimum length for Ether
	JNB	OKLEN
	MOV	CX,60		; make sure size at least 60
OKLEN:
;
;  Copy Packet : 
;
;	DS:SI = real data
;	ES    = shared memory address
;	CX    = number of bytes
;
	mov	al,0c9h		; reset the gate array
	wr_wd8	GACFR
;
	push	cx		; save xmit length
	xor	ax,ax
	mov	ah,TSTRT_PG	; starting page number
	mov	di,ax		; ES:DI = shared mem buff start
	shr	cx,1		; gonna do 16 bits
	jnc	evenx
	movsb			; pick up odd byte
evenx:
	rep	movsw		; copy all data into xmit buf
;
	mov	al,0
	wr_wd8	TCR
;
;  set up xmit length registers
;
	pop	cx		; len restored in cx
	pop	ds
	mov	al,cl		; length
	wr_wd8	TBCR0			; lower byte to TBCR0
	mov	al,ch
	wr_wd8	TBCR1			; higher byte to TBCR1
;
; set page number
;
	mov	al,TSTRT_PG
	wr_wd8	TPSR			; write start page into TPSR
;
; issue tx command
;
	mov	al,24h
	wr_wd8	CMDR			; start xmit
;
;  check to see if the last packet xmitted ok
;
	xor	cx,cx			; set a timeout
;
waitxmit:
;
	rd_wd8	CMDR		; command register
	and	al,4		; xmit bit
	jz	oktogo		; xmit is finished
	loop	waitxmit	; waiting for xmit to complete
	mov	ax,-1
    jmp SHORT getout
oktogo:
	xor	ax,ax
;
; go back for more
;
getout:
	pop	es
	POP	BP
	RET
ifdef	Microsoft
_E4XMIT	ENDP
else
E4XMIT	ENDP
endif

;
;
;*************************************************************************
;  ETUPDATE
;      update pointers and/or restart receiver when read routine has
;      already removed the current packet
;
;   usage:  etupdate();
;
ifdef	Microsoft
_E4ETUPDATE	PROC	FAR
else
E4ETUPDATE	PROC	FAR
endif

	PUSH 	ES
ifdef	Microsoft
	MOV	AX,word ptr [_BUFPT+2]	; establish data segment to buffer
else
	MOV	AX,word ptr [BUFPT+2]	; establish data segment to buffer
endif
	MOV	ES,AX		; put that in es
;
ifdef	Microsoft
	MOV	BX,_BUFREAD	; where read pointer is now
else
	MOV	BX,BUFREAD	; where read pointer is now
endif
	MOV	DX,ES:[BX]	; get size of this packet
	INC	DX
	INC	DX		; two more for length value

	ADD	BX,DX		; increment bufread by size of packet

ifdef	Microsoft
	MOV	CX,_BUFEND	; right before 2K safety area
else
	MOV	CX,BUFEND	; right before 2K safety area
endif
	CMP	BX,CX		; see if pointer is over limit
	JB	NOWRAPRD	; we are not at wrap-around

ifdef	Microsoft	
	MOV	BX,_BUFORG	; wrap to here
NOWRAPRD:
	MOV	_BUFREAD,BX	; buffer pointer has been updated
else
	MOV	BX,BUFORG	; wrap to here
NOWRAPRD:
	MOV	BUFREAD,BX	; buffer pointer has been updated
endif

;
;  DECREMENT TOTAL BUFFER SIZE
;
	CLI			; keep interrupt handler from bothering dec
ifdef	Microsoft
	MOV	CX,_BUFBIG	; size before removing packet
	SUB	CX,DX		; remove size of current packet
	MOV	_BUFBIG,CX	; put it back
else
	MOV	CX,BUFBIG	; size before removing packet
	SUB	CX,DX		; remove size of current packet
	MOV	BUFBIG,CX	; put it back
endif
	STI
;
;  IF RECEIVER IS ON, THEN CHECKING BUFLIM IS UNNECESSARY.
;
	MOV	AL,DEAF		; is the receiver turned off?
	OR	AL,AL		; 0 = reading, 1 = deaf
	JZ	ALIVE
;
;  CHECK FOR ROOM IN THE BUFFER, IF THERE IS, TURN ON RECEIVER
;
ifdef	Microsoft
	MOV	AX,_BUFLIM	; what is our limit?
else
	MOV	AX,BUFLIM	; what is our limit?
endif
	CMP	CX,AX		; compare to limit
	JA	ALIVE		; not really alive, but can't turn on yet

	XOR	AL,AL
	MOV	DEAF,AL		; reset flag

	INC	OFFS		; keep count how many times this happened

;
;  turn receiver back on
;

ALIVE:
	POP	ES
	RET	

ifdef	Microsoft
_E4ETUPDATE	ENDP
else
E4ETUPDATE	ENDP
endif

ifdef Microsoft
;_TEXT	ends
else
	ENDPS
endif
	END
[ RETURN TO DIRECTORY ]