Metropoli BBS
VIEWER: net501.asm MODE: TEXT (ASCII)
;  3COM 3C501 driver code
;  Tim Krauskopf
;
;  Thanks to Bruce Orchard for mods to allow more I/O addresses and INTs
;  5/18/88
;****************************************************************************
;*                                                                          *
;*                                                                          *
;*      part of NCSA Telnet                                                 *
;*      by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer     *
;*                                                                          *
;*      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
;
;  Will read and write packets from the 2K packet buffer on the
;  Etherlink card.  Provides hooks for higher layer protocols.
;
;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	NET
  	INCLUDE	DOS.MAC
  	SETX
endif
;
;  Equates for controlling the 3COM board
;
ICTRL	EQU	020H		; 8259 interrupt control register
IMASK	EQU	021H		; 8259 interrupt mask register
ENDOFI	EQU	020H		; end-of-interrupt
;
;  Controller registers
;
O_EADDR	EQU	0H		; Network address for hardware checking
;   takes six bytes, this is the address that the Ethernet board will
;   match to find packets on the net.  (0-5h)
;
O_EREC	EQU	6H		; Receive status (read)
				; Receive command (write)
O_ESEND	EQU	7H		; Transmit status (read)
				; Transmit command (write)
O_EGLOW	EQU	8H		; General purpose pointer for R/W to
				; packet buffer, low byte
O_EGHI	EQU	9H		; high byte, total of 11 bits for 2K buffer
O_ERLOW	EQU	0AH		; Receive pointer, set by board (read) low byte
				; Receive buffer pointer clear (write)
O_ERHI	EQU	0BH		; high byte of Receive pointer
O_EPROM	EQU	0CH		; PROM window address
O_XXX	EQU	0DH 		; ??
O_EAUX	EQU	0EH		; Auxiliary Status (read)
				; Aux Command (write)
O_EBUF	EQU	0FH		; Buffer window (where to I/O to net)
;
;  Transmit command options
;     what conditions we wish to be interrupted on after transmit
;
EDTUNDER  EQU	01H		; Detect underflow (bad CRC), not used
EDTCOLL	EQU	02H		; Detect collision on xmit
EDTC16	EQU	04H		; Detect 16 consecutive collisions (net down)
EDTOK	EQU	08H		; Detect successful transmission
				; other 4 bits unused in this reg
EXMITNORM  EQU	00H		; Normal use for interrupt-driven XMIT
;
;  Transmit status results
;
;  Use same values as commands, 08h means OK to xmit again
;
;*****************************
;  Receive Command Options
;
;    If something is not detected for, the receiver automatically discards
;        those packets.
;
EDTOVER	EQU	01H		; Detect Overflow condition
EDTFCS	EQU	02H		; Detect FCS error, bad CRC on packet
EDTDRB	EQU	04H		; Detect dribble errors and accept them
EDTSHORT EQU	08H		; Detect short frames (< 60 bytes)
EDTEOF	EQU	10H		; Detect no overflow (end-of-frame found)
EGOOD	EQU	20H		; Accept good frames 
; four values legal for the last two bits:
ECLOSE	EQU	00H		; Turn off receiver
EGETALL	EQU	40H		; Get all packets, no matter what address
EBROAD	EQU	80H		; get those for me or for broadcast
EMULTI	EQU	0C0H		; get those for me or for multicast
EWANT	EQU	0A0h		; EGOOD OR EBROAD
				; which packets we will look for on net
;
;  Receive Status results
;
;  errors are not detected unless asked for...otherwise the board just
;  won't take bad packets off of the net.
;
ERROVER	EQU	01H		; overflow error
ERRFCS	EQU	02H		; FCS (checksum) error
ERRDRB	EQU	04H		; Dribble error
ERRSHORT  EQU	08H		; Short frame error
ENOOVER	EQU	10H		; Received without overflow error 
				; means that we didn't miss any by being slow
;EGOOD	EQU	20H		; as above, we received a valid frame
; undefined 40h
ESTALE	EQU	80H		; stale receive condition, already read me
;
;  Aux command register
;
EIRE	EQU	01H		; interrupt request enable (no DMA) new boards
EBADFCS	EQU	02H		; create bad checksum for testing only
;
;  Next two bits tell who has access to packet buffer
;
EBUS	EQU	00H		; System bus has control of buffer
EXMIT	EQU	04H		; Transmit packet in buffer, auto kick
				; back to recieve status
EGETEM	EQU	08H		; Receive state, look for packets
ELOOP	EQU	0CH		; Transmit and loopback into xmit buffer
;  10H  unused
EDMA	EQU	20H		; Starts a DMA xfer
ERIDE	EQU	40H		; Interrupt and DMA enable
ERESET	EQU	80H		; Reset the Ethernet board
;
;  Aux status register
;
ERBUSY	EQU	01H		; Receive busy, receiver looking for packets
;               02H		; echos command status EBADFCS
;               04,08h		; echos command status for EXMIT,EGETEM,EBUS
EDMADONE  EQU	10H		; goes to one when DMA finishes
;               20H		; echos DMA request bit
;               40h		; echos RIDE bit
EXBUSY	EQU	80H		; for polled xmit, goes to 0 when xmit is done
;
;
;  Macros for in and out
;
MOUT	MACRO	REG,STUFF       ; one byte to the given I/O register
	MOV	DX,REG
	MOV	AL,STUFF
	OUT	DX,AL
	ENDM
;
MOUTW	MACRO	REG,LO,HI  	; two bytes to the I/O double port
	MOV	DX,REG
	MOV	AL,LO
	OUT	DX,AL
	INC	DX
	MOV	AL,HI
	OUT	DX,AL
	ENDM
;
MIN	MACRO	REG         	; get one byte to al
	MOV	DX,REG
	IN	AL,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
;
;
BASEA	DW	?		; Base I/O address on PC I/O bus
EADDR	DW	?		; Network address for hardware checking
;   takes six bytes, this is the address that the Ethernet board will
;   match to find packets on the net.  (0-5h)
;
EREC	DW	?		; Receive status (read)
				; Receive command (write)
ESEND	DW	?		; Transmit status (read)
				; Transmit command (write)
EGLOW	DW	?		; General purpose pointer for R/W to
				; packet buffer, low byte
EGHI	DW	?		; high byte, total of 11 bits for 2K buffer
ERLOW	DW	?		; Receive pointer, set by board (read) low byte
				; Receive buffer pointer clear (write)
ERHI	DW	?		; high byte of Receive pointer
EPROM	DW	?		; PROM window address
EAUX	DW	?		; Auxiliary Status (read)
				; Aux Command (write)
EBUF	DW	?		; Buffer window (where to I/O to net)


SAVECS	DW	00H		; where to save the old interrupt ptr
SAVEIP	DW	00H
OLDMASK	DB	00H		; save interrupt controller mask
DEAF	DB	00H		; when we can't handle any more packets
OFFS	DW	00H		; how many times the handler was turned off
;
;  use variables to access IRQ3 or IRQ5
;  3 is COM2, 5 is LPT2
;
CINTNUM	db	3
INTNUM	DB	0BH		; Defaults to IRQ3, interrupt handler 0bh
WHICHINT  DW	4*0BH		; ETOPEN can change these values
TURNOFF	DB	08H
TURNON	DB	0F7H
ifdef Microsoft
;_DATA	ends
else
	ENDDS
endif
;
;
;
;   The subroutines to call from C
;
ifdef Microsoft
;_TEXT	segment	public	'CODE'
;	assume CS:_TEXT
	.code
	PUBLIC	_E1RECV,_E1ETOPEN,_E1ETCLOSE,_E1GETADDR
ifdef NOT_USED
    PUBLIC  _E1SETADDR
endif
    PUBLIC  _E1XMIT,_E1ETUPDATE
else
	PSEG
	PUBLIC	E1RECV,E1ETOPEN,E1ETCLOSE,E1GETADDR
ifdef NOT_USED
    PUBLIC  E1SETADDR
endif
    PUBLIC  E1XMIT,E1ETUPDATE
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, base mem address (unused) and
;                i/o address to use
;
;
ifdef Microsoft
_E1ETOPEN	PROC	FAR
else
E1ETOPEN	PROC	FAR
endif
	PUSH	BP
	MOV	BP,SP
	PUSH	SI
;
;  set up all of the I/O register values in memory
;
	MOV	AX,[BP+X+8]	; i/o address -> ax
	MOV	BASEA,AX	; save base address
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EADDR	; + offset of Ethernet address
	MOV	EADDR,BX	; store address of Ethernet address
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EREC	; + offset of receive command/status
	MOV	EREC,BX		; store address of receive command status
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_ESEND	; + offset of transmit command/status
	MOV	ESEND,BX	; store address of transmit command status
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EGLOW	; + offset of general pointer low byte
	MOV	EGLOW,BX	; store address of general pointer low byte
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EGHI	; + offset of general pointer high byte
	MOV	EGHI,BX		; store address of general pointer high byte
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_ERLOW	; + offset of receive pointer low byte
	MOV	ERLOW,BX	; store address of receive pointer low byte
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_ERHI	; + offset of receive pointer high byte
	MOV	ERHI,BX		; store address of receive pointer high byte
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EPROM	; + offset of PROM window
	MOV	EPROM,BX	; store address of PROM window
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EAUX	; + offset of auxiliary command/status
	MOV	EAUX,BX		; store address of auxiliary command/status
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EBUF	; + offset of buffer window
	MOV	EBUF,BX		; store address of buffer window

;
;  check the parameters for interrupt and dma
;
	MOV	AX,[BP+X+4]	; interrupt number
	or	ax,ax
	JG	IOK1		; if 0 or negative, use 3
	mov	ax,3
IOK1:
	CMP	AX,7		; too big?
	JNG	IOK2		; yes:  use 3
	mov	ax,3
IOK2:
	MOV	CINTNUM,AL	; save 8259 interrupt number
	ADD	AX,8		; convert to 8086 vector number
	MOV	INTNUM,AL	; save vector number
	SHL	AX,1		; * 2
	SHL	AX,1		; * 2 = vector address
	MOV	WHICHINT,AX	; save vector address
	MOV	CL,CINTNUM	; interrupt number -> cl
	MOV	AX,1		; 1 -> ax
	SHL	AX,CL		; make interrupt mask
	MOV	TURNOFF,AL	; store interrupt disable mask
	NOT	AX		; make enable mask
	MOV	TURNON,AL	; store interrupt enable mask

;
;  DMA not used for 3C501
;
	MOUT	EAUX,ERESET	; reset the board
	MOUT	EAUX,0          ; Clear the reset bit, otherwise keeps resetting
;
;  install the interrupt handler
;
	CALL	IINST		; do the patching of the interrupt table
;
;  set up the net address
;
	MOV	DX,EADDR	; get base i/o reg for setting address

	PUSH 	DS		; save mine
	MOV	AX,[BP+X+2]	; get new one
	MOV	DS,AX           ; set new one
	MOV	SI,[BP+X]	; get pointer, ds:si is ready
	;
	MOV	CX,6
	CLD
SADDR:
	LODSB			; get next one
	OUT	DX,AL		; send it
	INC	DX		; next position
	LOOP	SADDR		; do 6 times

	POP	DS		; get back DS of local data
;
;  enable interrupts here with interrupt handler 
;  already set up.
;
	MOUT	ESEND,0		; xmit command = 0 for no interrupts
	IN	AL,DX

	MOUT	EREC,EWANT	; Set receiver for which packets we want
	IN	AL,DX		; reset 'stale'

	MOUT	ERLOW,0		; Clear the receive buffer pointer

	CLI
	MOUT	EAUX,EGETEM+ERIDE	; Set for receive, interrupts

	MIN	IMASK		; get current int enable mask
	MOV	BL,AL		; save a copy
	AND	AL,TURNON	; force bit for etherlink board off
	OUT	DX,AL		; put back the byte, IRQ enabled

	STI
	AND	BL,TURNOFF	; isolate this bit only from oldmask
	MOV	OLDMASK,BL	; save it
;
	POP	SI
	POP	BP
	XOR	AX,AX
	RET
ifdef Microsoft
_E1ETOPEN	ENDP
else
E1ETOPEN	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 (unused)
;             int ioa;             io address for board (unused)
;
ifdef Microsoft
_E1SETADDR	PROC	FAR
else
E1SETADDR	PROC	FAR
endif
	ret
;	PUSH	BP
;	MOV	BP,SP
;	PUSH	SI
;	PUSH	DS
;	MOV	AX,[BP+X+2]
;	MOV	DS,AX
;	MOV	SI,[BP+X]	; address of buffer to read
;
;	MOV	CX,6
;	MOV	DX,EADDR	; get base i/o reg for setting address
;	CLD
SADDR2:
;	LODSB			; get next one
;	OUT	DX,AL		; send it
;	INC	DX		; next position
;	LOOP	SADDR2		; do 6 times
;
;	POP	DS
;	POP	SI
;	POP	BP
;	RET
ifdef Microsoft
_E1SETADDR	ENDP
else
E1SETADDR	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;     (unused here) mem address and ioaddress to use
;
ifdef Microsoft
_E1GETADDR	PROC	FAR
else
E1GETADDR	PROC	FAR
endif
	PUSH	BP
	MOV	BP,SP
	PUSH	DI
	PUSH 	ES		; save mine
	MOV	AX,[BP+X+2]	; get new one
	MOV	ES,AX           ; set new one
	MOV	DI,[BP+X]	; get pointer, es:di is ready
	;
;
;  set up all of the I/O register values in memory
;
	MOV	AX,[BP+X+6]	; i/o address -> ax
	MOV	BASEA,AX	; save base address
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EADDR	; + offset of Ethernet address
	MOV	EADDR,BX	; store address of Ethernet address
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EREC	; + offset of receive command/status
	MOV	EREC,BX		; store address of receive command status
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_ESEND	; + offset of transmit command/status
	MOV	ESEND,BX	; store address of transmit command status
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EGLOW	; + offset of general pointer low byte
	MOV	EGLOW,BX	; store address of general pointer low byte
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EGHI	; + offset of general pointer high byte
	MOV	EGHI,BX		; store address of general pointer high byte
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_ERLOW	; + offset of receive pointer low byte
	MOV	ERLOW,BX	; store address of receive pointer low byte
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_ERHI	; + offset of receive pointer high byte
	MOV	ERHI,BX		; store address of receive pointer high byte
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EPROM	; + offset of PROM window
	MOV	EPROM,BX	; store address of PROM window
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EAUX	; + offset of auxiliary command/status
	MOV	EAUX,BX		; store address of auxiliary command/status
	MOV	BX,AX		; Base address -> BX
	ADD	BX,O_EBUF	; + offset of buffer window
	MOV	EBUF,BX		; store address of buffer window
;
;
	MOV	BX,0            ; start location 
	MOV	CX,EPROM	; address window
GADDR:
	CLD
	MOUTW	EGLOW,BL,BH  	; set gp to the right value
	MIN	CX		; get value from prom address window
	STOSB                   ; put into given buffer
	INC	BX		; next position
	CMP	BX,6
	JNZ 	GADDR  		; do 6 times
	POP 	ES
	POP	DI
	POP	BP		
	xor	ax,ax
	RET
ifdef Microsoft
_E1GETADDR	ENDP
else
E1GETADDR	ENDP
endif
;
;***********************************************************************
;  ETCLOSE
;        shut it down, remove the interrupt handler
;
;  usage:  etclose();
;
;
ifdef Microsoft
_E1ETCLOSE	PROC	FAR
else
E1ETCLOSE	PROC	FAR
endif
	CLI
	MOUT	EAUX,ERESET	; Turn off all pendings, cause reset
	MOUT	EAUX,0          ; Turn off reset
;
;
;  mask out IRQ on interrupt controller
;
	MIN	IMASK		; get current mask
	OR	AL,TURNOFF	; force that bit on
	OUT	DX,AL		; send it back to controller
	STI

	CALL	DEINST		; restore old interrupt handler

	MOV	BL,OLDMASK	; get back saved setting of irq
	NOT	BL		; flip it
	CLI
	MIN	IMASK
	AND	AL,BL		; restore setting of that bit
	OUT	DX,AL
	STI	
	xor	ax,ax
	RET
ifdef Microsoft
_E1ETCLOSE	ENDP
else
E1ETCLOSE	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)
;
;   The 3COM 3C501 version uses interrupts, so this routine is a NOP
;   for this board.
;
;    usage:  recv();
;
ifdef Microsoft
_E1RECV	PROC	FAR
else
E1RECV	PROC	FAR
endif
	RET			; for compatibility with other drivers
ifdef Microsoft
_E1RECV	ENDP
else
E1RECV	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
_E1XMIT	PROC	FAR
else
E1XMIT	PROC	FAR
endif
	PUSH	BP
	MOV	BP,SP
	PUSH	SI
	PUSH	DI
	MOV	SI,[BP+X]	; offset for buffer

	MOV	AX,[BP+X+4]	; count of bytes
	MOV	CX,AX		; save a copy, might be less than 60, ok

	CMP	AX,60		; minimum length for Ether
	JNB	OKLEN
	MOV	AX,60		; make sure size at least 60
OKLEN:
	MOV	BX,2048		; total length of buffer
	SUB	BX,AX		; offset of for buffer pointer to start
	MOV	DI,BX		; save a copy of the buffer pointer
;
;  TAKE CONTROL OF THE INPUT BUFFER
;
	MOUT	EAUX,EBUS+ERIDE	; take buffer away from receiver
	MOUT	ERLOW,0		; clear receive pointer for next read
	MOUTW	EGLOW,BL,BH	; set the general purpose pointer

	MOV	DX,EBUF		; window to packet buffer
	PUSH	DS		; set up proper ds for the buffer
	MOV	AX,[BP+X+2]
	MOV	DS,AX
	CLD
FILLBUF:
	LODSB			; get value to go into buffer
	OUT	DX,AL		; put it into buffer (autoincrement)
	LOOP	FILLBUF		; do whole count

	POP	DS
;
;  packet is in buffer, ready to be sent
;
TRYAGAIN:
	MOV	BX,DI		; retrieve copy of offset pointer
	MOUTW	EGLOW,BL,BH	; set the general purpose pointer (again)
;
	MOUT	EAUX,EXMIT+ERIDE    ; tell the board to send it and start receiving
	
NOTDONEX:
	MIN	EAUX		; waiting for transmit to finish
	AND	AL,EXBUSY	; is it done yet?
	JNZ	NOTDONEX	; no, wait some more

	MOV	CX,0		; return value, ok
	MIN	ESEND		; get xmit status
	MOV	BL,AL		; save status
	AND	AL,EDTOK	; was it ok?
	JNZ	DONEX		; yes, successful xmit
;
;  handle the possible errors, return 1 on coll16
;     coll16 generally means that the network has failed
;
	MOV	AL,BL		; get copy of status back
	AND	AL,EDTC16	; check collision 16
	JNZ	RET16		; yes, network probably down
	MOV	AL,BL		; get copy back again
	AND	AL,EDTCOLL	; check for collision status
	JZ	UNK		; no, unknown problem
	MOUT	EAUX,EBUS+ERIDE		; collision, reset buffer control
	JMP	TRYAGAIN	; go for it
UNK:
	MOV	CX,2		; unknown problem return code
    JMP SHORT DONEX
RET16:
	MOV	CX,1		; failure return
DONEX:
	MOUT	EREC,EWANT	; reset receive register filter necessary
	MIN	EAUX	
	AND	AL,ERBUSY	; is it still in receive state or done?
	JNZ	DONEMIT		; not ready now, return instead

	MOV	AL,INTNUM
	CMP	AL,0BH		; two choices of int to call
	JNZ	TRYNINT
	INT	0BH		; we do have a packet, read it
    JMP SHORT DONEMIT
TRYNINT:
	CMP	AL,0DH
	JNZ	DONEMIT
	INT	0DH

DONEMIT:
	MOV	AX,CX		; put return in ax
	POP	DI
	POP	SI
	POP	BP
	RET
ifdef Microsoft
_E1XMIT	ENDP
else
E1XMIT	ENDP
endif
;
;*************************************************************************
;  Interrupt Handler
;  installation and deinstallation
;
;     the handler takes the receive packet out of the input buffer
;
DEINST	PROC	NEAR
	MOV	CX,SAVEIP	; get old ip from save spot
	MOV	DX,SAVECS	; get old cs from save spot
	MOV	BX,WHICHINT	; interrupt in table for 3com board
	PUSH	DS
	XOR	AX,AX		; system interrupt table
	MOV	DS,AX		
	CLI
	MOV	[BX],CX		; store old ip into the table
	INC	BX
	INC	BX		; move pointer in interrupt table
	MOV	[BX],DX		; store old cs into the table
	STI
	POP	DS
	RET
DEINST	ENDP
;
IINST	PROC	NEAR
	MOV	CS:MYDS,DS	; store for use by handler
	MOV	BX,WHICHINT	; interrupt in table for 3com board
	PUSH	DS
	XOR	AX,AX		; system interrupt table
	MOV	DS,AX		
	MOV	AX,OFFSET IHAND	; where the handler is
	CLI
	MOV	DX,[BX]		; keep copy of the ip
	MOV	[BX],AX		; store ip into the table
	INC	BX
	INC	BX		; move pointer in interrupt table
	MOV	CX,[BX]		; keep copy of the cs, too
	MOV	AX,CS
	MOV	[BX],AX		; store new cs into the table
	STI
	POP	DS
	MOV	SAVEIP,DX	; store them away
	MOV	SAVECS,CX
	RET
MYDS	DW	00H		; the data segment for this assembly code
ICNT    DB      00H
IHAND:			       	; not a public name, only handles ints
	STI
	PUSH	DS
	PUSH 	ES
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	DI
	CLD			; all moves will be forward
;
;  SET UP CORRECT DS
;
	MOV	DS,CS:MYDS		; get correct ds
ifdef Microsoft
	MOV	AX,word ptr [_BUFPT+2]	; buffer's ds
	MOV	DI,_BUFPT		; where buffer is
else
	MOV	AX,word ptr [BUFPT+2]	; buffer's ds
	MOV	DI,BUFPT		; where buffer is
endif
	MOV	ES,AX
;
;  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.  turn off receiver
;
	MOUT	EAUX,EBUS+ERIDE	; refuse to read more packets until restarted

	MIN	EREC		; must clear interrupt

	MOV	AL,1		; set flag
	MOV	DEAF,AL		; we are now deaf, read routine must restart

    JMP SHORT ENDINT      ; can't do much, we lose packets until restarted

;
;  wrap pointer around at end, we know that we have room
;
ISROOM:
ifdef Microsoft
	MOV	DX,_BUFEND	; right before 2K safety area
else
	MOV	DX,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,_BUFORG	; wrap to here
	MOV	_BUFPT,AX	; wrap-around
else
	MOV	AX,BUFORG	; wrap to here
	MOV	BUFPT,AX	; wrap-around
endif
	MOV	DI,AX		; di also
;
;  here, DI contains where we want to put the packet.
;
OKAYREAD:

;
IREADONE:
	MOUT	EAUX,EBUS+ERIDE	; turn off receive, give buffer to bus
	MOUTW	EGLOW,0,0	; clear general purpose pointer for read
	MIN	EREC		; get status to al, clears read

	MOV	DX,ERLOW	; receive buffer pointer
	IN	AL,DX
	MOV	CL,AL		; save low byte
	INC	DX
	IN	AL,DX
	MOV	CH,AL		; save high byte

	MOV	BX,CX           ; save another copy of the length
	OR	BX,BX		; check for non-zero
	JZ	STOPINT		; no packet
	
	MOV	AX,BX		; save length in buffer, before packet
	STOSW

	MOV	DX,EBUF		; window to the data

IDOBYTES:
	IN	AL,DX		; get a byte
	STOSB			; save it to es:di
	LOOP 	IDOBYTES
;
;
;  DI now contains updated value for BUFPT, BX contains size of packet
;

ifdef Microsoft
	MOV	_BUFPT,DI	; it is here, now
	MOV	AX,_BUFBIG	; total amount of stuff in buffer
else
	MOV	BUFPT,DI	; it is here, now
	MOV	AX,BUFBIG	; total amount of stuff in buffer
endif
	ADD	AX,BX
	INC	AX
	INC	AX		; to cover the length value
ifdef Microsoft
	MOV	_BUFBIG,AX	; after adding in current packet size
else
	MOV	BUFBIG,AX	; after adding in current packet size
endif
;
;  signs that something is actually happening - used for debugging
;
;	MOV	AX,0B000H       ; screen
;	MOV	ES,AX
;	MOV	DI,3998		; lower right corner
;	INC	CS:ICNT
;	MOV	Al,CS:ICNT	; character
;	STOSB

;
;  set up to read the next packet from the net
;
STOPINT:
	MOUT	ERLOW,0		; clear receive buffer pointer
	MOUT	EAUX,EGETEM+ERIDE	; set receive bit in aux

ENDINT:

	MOUT	ICTRL,ENDOFI	; signal end of interrupt
	POP	DI
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	POP	ES
	POP	DS
	IRET
IINST	ENDP

;
;*************************************************************************
;  ETUPDATE
;      update pointers and/or restart receiver when read routine has
;      already removed the current packet
;
;   usage:  etupdate();
;
ifdef Microsoft
_E1ETUPDATE	PROC	FAR
else
E1ETUPDATE	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
else
	MOV	BX,BUFORG	; wrap to here
endif
NOWRAPRD:
ifdef Microsoft
	MOV	_BUFREAD,BX	; buffer pointer has been updated
else
	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

	MOUT	ERLOW,0		; reset receive buffer ptr
	MOUT	EAUX,EGETEM+ERIDE	; turn on receiver

ALIVE:
	POP	ES
	RET	
ifdef Microsoft
_E1ETUPDATE	ENDP
else
E1ETUPDATE	ENDP
endif

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