;History:180,18
;Sun Jan 05 22:13:57 1992 increased RST_IVAL from 4 to 7.
;Tue Feb 27 10:56:15 1990 send_pkt wasn't timing out properly.
version	equ	1

	include	defs.asm

;Ported from Philip Prindeville's arcnet driver for PCIP
;by Russell Nelson.  Any bugs are due to Russell Nelson.

;Ported from Philip Prindevilles's and Russell Nelson's ARCNET
;driver to RFC1201 and class Ethernet by Martin Wilmes (Q91@DHDURZ1.BITNET).
;So any bugs are now due to Martin Wilmes.

;  Parts Copyright, 1988-1992, Russell Nelson, Crynwr Software
;  Copyright 1991 Martin Wilmes

;   This program is free software; you can redistribute it and/or modify
;   it under the terms of the GNU General Public License as published by
;   the Free Software Foundation, version 1.
;
;   This program is distributed in the hope that it will be useful,
;   but WITHOUT ANY WARRANTY; without even the implied warranty of
;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;   GNU General Public License for more details.
;
;   You should have received a copy of the GNU General Public License
;   along with this program; if not, write to the Free Software
;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

code	segment	word public
	assume	cs:code, ds:code

;Registers:

;the following I/O addresses are mapped to the COM 9026
IMASK		equ	0		; writeable
STATUS		equ	0		; readable
COMMAND		equ	1
;the following I/O addresses are mapped to the 8253 counter/timer
CNTR0		equ	4
CNTR1		equ	5
CNTR2		equ	6
MODE		equ	7
;reading the following I/O addresse performs a software reset.
SW_RST		equ	8

; time needed to do various things (in clock ticks)
RST_IVAL	equ	7		;reset
SEND_IVAL	equ	4		;send
ACK_IVAL        equ     4               ;acknowledge

; Maximum number of replays of an unacknowledged packet
SEND_REPLAY     equ     5               ;times a packet should be replayed
                                        ;when we get no acknowledge, not
                                        ;yet used

; ARP type for ARCnet
ARP_ARC		equ	001h           ; We are ARCnet but show up as EtherNet

; broadcast address is nid 0
ARC_BCAST	equ	0              ;ARCnet Broadcast address

; packet sizes
ARC_MTU		equ	249            ;These are the length of client data!
ARC_MnTU	equ	252            ;
ARC_XMTU	equ	504            ;
;
;status/interrupt mask bit fields
;
ST_TA		equ	001h		; transmitter available
ST_TMA		equ	002h		; transmitted msg. ackd
ST_RECON	equ	004h		; system reconfigured
ST_TEST		equ	008h		; test flag
ST_POR		equ	010h		; power-on-reset
ST_ETS1		equ	020h		; unused
ST_ETS2		equ	040h		; unused
ST_RI		equ	080h		; receiver inhibited

;
;in the command register, the following bits have these meanings:
;		0-2	command
;		3-4	page number (enable rvc/xmt)
;		 7	rcv b'casts


DSBL_XMT	equ	001h		; disable transmitter
DSBL_RCV	equ	002h		; disable receiver
ENBL_XMT	equ	003h		; enable transmitter
ENBL_RCV	equ	004h		; enable receiver
DFN_CONF	equ	005h		; define configuration
CLR_FLGS	equ	006h		; clear flags
LD_TST_FLG	equ	007h		; load test flags

; flags for clear flags operation

FL_POR		equ	008h		; power-on-reset
FL_RECON	equ	010h		; system reconfigured

; flags for load test flags operation

FL_TST		equ	008h		; test flag (diagnostic)

; byte deposited into first address of buffers when POR
TSTWRD		equ	0321Q

; handy macros for enable receiver/transmitter

BCAST		equ	080h		; receiver only

; flags for define configuration

CONF_NORM	equ	000h		; 1-249 byte packets
CONF_XTND	equ	008h		; 250-504 byte packets


         public    no_confident
NO_CONFIDENT    db      1

; designations for receiver/transmitter buffers.  sorry, no cleverness here
RCVPAGE		equ	0
XMTPAGE		equ	3

; Flag which indicates that we should build and expect 802.3 framed Novell
; IPX packets from the IPX-Shell, set to N_OPTION in this case
OPTION_8023     db      0

; Maximum number of fragments we can store in our receive-buffer
MAX_FRAGMENTS   equ     3

; Data for sending splitted packets

send_did        db      0                   ;Destination ID
send_sid        db      0                   ;Source ID
Stored_CX       dw      0                   ;Rest of packet not send yet
Protocol_ID     db      0                   ;The ARCnet protocol ID
Split_Number    db      0                   ;Number of fragments we have to send
Pkt_Number      db      0                   ;Number of the current packet
Sequence_Number dw      0                   ;Sequence number, start with zero
send_times      db      0                   ;times a packet has been replayed

; Data for receiving splitted packets

last_expected      db      0feh             ;Splitflag of last Packet in a
                                            ;sequence - 1
expected_packet    db      0                ;Used to test a sequence
expected_sequence  dw      0
recv_protocol      dw      0                ;Ethernet protocol
recv_num_frags     db      0                ;Number of fragments we received
recv_protocolbyte  db      0                ;ARCnet protocol
recv_offset        dw      0
recv_packet        dw      0
last_length        dw      0

; We store all but the last fragments of incomplete sequences in our own
; buffer. This is not necessary since the ARCnet card has 4 pages and we
; should receive a maximum of 3 fragments in a sequence. Using our own
; buffer has two advantages: Our driver becomes faster because it uses
; idle times when the packet not complete to test the sequence and
; reassemble the client data and our own buffer makes it easier to
; change the driver to receive more than 3 fragments if required.
recv_buffer        db      (MAX_FRAGMENTS-1)*504 dup (0feh)

	public	int_no
int_no		db	5,0,0,0		; interrupt number.
io_addr		dw	02e0h,0		; I/O address for card (jumpers)
mem_base	dw	0d800h,0

	public	driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class	db	1,0,0           ;We show as Ethernet driver, class 1.
                                        ;when the OPTION_8023 flag is set, we
                                        ;don not refuse calls for a class 11
                                        ;driver since the PDIPX-shell asks for
                                        ;a class 11 driver
driver_type	db	14       	;Datapoint RIM (from the packet spec)
driver_name	db	'ARCEther',0	;name of the driver.
driver_function	db	2
parameter_list	label	byte
	db	1	;major rev of packet driver
	db	9	;minor rev of packet driver
	db	14	;length of parameter list
	db	6	;length of MAC-layer address
	dw	1514	;MTU, including MAC headers
	dw	MAX_MULTICAST * EADDR_LEN	;buffer size of multicast addrs
	dw	0	;(# of back-to-back MTU rcvs) - 1
	dw	0	;(# of successive xmits) - 1
int_num	dw	0	;Interrupt # to hook for post-EOI
			;processing, 0 == none,

	public	rcv_modes
rcv_modes	dw	4		;number of receive modes in our table.
		dw	0,0,0,rcv_mode_3

	include	popf.asm
	include	movemem.asm

	public bad_command_intercept
bad_command_intercept:
;called with ah=command, unknown to the skeleton.
;exit with nc if okay, cy, dh=error if not.
	mov	dh,BAD_COMMAND
	stc
	ret

	public	as_send_pkt
; The Asynchronous Transmit Packet routine.
; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
;   interrupts possibly enabled.
; Exit with nc if ok, or else cy if error, dh set to error number.
;   es:di and interrupt enable flag preserved on exit.
as_send_pkt:
	ret

	public	drop_pkt
; Drop a packet from the queue.
; Enter with es:di -> iocb.
drop_pkt:
	assume	ds:nothing
	ret

	public	xmit
; Process a transmit interrupt with the least possible latency to achieve
;   back-to-back packet transmissions.
; May only use ax and dx.
xmit:
	assume	ds:nothing
	ret


	public	send_pkt
send_pkt:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
;  (only if the high-performance bit is set in driver_function)
;enter with ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
	assume	ds:nothing

	cmp	cx,GIANT		; Is this packet too large?
	ja	send_pkt_toobig

        push    ds
        push    si
        push    cx

        mov     send_did,0		;Assume that destination is broadcast
        mov     cx,3
        mov     ax,ds:[si+4]
        cmp     ax,0ffffh		;A first fast check for non-Broadcast
        jne     send_non_broadcast
send_adress_loop:
        lodsw
        cmp     ax,0ffffh
        jne     send_after_loop
        loop    send_adress_loop
        jmp     nr_3			;destination is indeed broadcast

send_pkt_toobig:
	mov	dh,NO_SPACE
	stc
	ret

send_after_loop:
        dec     cx                      ;read the rest of the ethernet adress
        rep     lodsw
send_non_broadcast:
        mov     cs:send_did,ah          ;arcnet address is highest byte of
                                        ;the ethernet address
nr_3:
        pop     cx
        pop     di
        pop     es                     ;We change ds:si to to es:di because we
                                       ;we want to access our own data more easily.
        mov     ax,cs
        mov     ds,ax
        assume  ds:code

        mov     al,es:[di+11]		;source ID
        mov     send_sid,ah
        mov     ax,es:[di+12]		;Ethernet Protocol
        cmp     ax,0008h		;IP Packet
        jne     nr_5
        mov     protocol_ID,212		;IP on ARCnet
        jmp     nr_10
nr_5:
        cmp     ax,0608h		;ARP packet
        jne     nr_6
        mov     protocol_ID,213		;ARP on ARCnet
        mov     cx,18+14		;18Bytes is ARP and RARP on Arcnet
					;14 Byte for EtherNet Header and Protocol
					;which we forget later
        jmp     nr_10
nr_6:
        cmp     ax,3580h		;RARP packet
        jne     nr_7
        mov     protocol_ID,214		;RARP on ARCnet
        mov     cx,18+14
        jmp     nr_10
nr_7:
        test    OPTION_8023,N_OPTION	;do we expect 802.3 packets?
        jnz     nr_7a
        cmp     ax,3781h		;No: check for Bluebox IPX protocol
        jne     nr_8
        mov     protocol_id,250		;IPX on ARCnet
        jmp     nr_10
nr_7a:
        mov     ax,es:[di+14]		;Every IPX packet begins with ffff.
        cmp     ax,0ffffh		;so we check the begin of the client
					;data for this signature
        jne     nr_8
        mov     protocol_ID,250
        jmp     nr_10

nr_8:
        mov     ax,es:[di+12]		;Ethernet Protocol
        cmp     ax,0CAACh		;LANSoft
        jne     nr_5
        mov     protocol_ID,251		;LANSoft on ARCnet
        jmp     nr_10
nr_error:				;We do not now this protocol and
        assume  ds:nothing		;cannot send
        mov     dh,CANT_SEND
        stc
        ret

nr_10:
        sub     cx,14			;Forget EtherNet-Header
        add     di,14
        inc     sequence_number
					;
					;Look if we can send it as one packet or have to split the packet
					;
        xor     al,al
        mov     Split_Number,0		;Number of splits is zero in case of one packet
        mov     pkt_number,0		;Fragment number is zero for the first packet
	cmp	cx,ARC_XMTU		;length of client data longer than long frame?
	jbe	new_send		;no, we can send it as one packet
        mov     split_number,1		;division takes so long, so we just
        cmp     cx,1008			;compare
        jbe     new_send
        mov     split_number,3
					;
;Wait for transmitter ready.
        ;
New_send:
        push    es			;save current position of paket
        push    di
New_send_2:
	loadport
	setport	STATUS

	mov	ax,SEND_IVAL		;only wait this long for it.
	call	set_timeout		;otherwise we can't send.
send_pkt_3:
	in	al,dx			;if not busy, exit.
	and	al,ST_TA
	jne	send_pkt_2
	call	do_timeout		;did we time out yet?
	jne	send_pkt_3		;no, not yet.

        loadport
	setport	COMMAND			;stop the transmit.
	mov	al,DSBL_XMT
	out	dx,al
	mov	dh,CANT_SEND		;timed out, can't send.
        pop     si
        pop     ds
        assume  ds:nothing
        stc
	ret

send_pkt_2:
;store the packet on the board.
	mov	es,mem_base
	mov	di,XMTPAGE * 512

        mov     al,send_sid
        mov     ah,send_did
	stosw				;move the SID and DID to the board.
        mov     Stored_CX,cx            ;remeber the packet length

        cmp     cx,ARC_MTU              ;Decide which frame we use
        jbe     Send_normal_1           ;normal frame
        cmp     cx,ARC_MNTU
        jbe     send_pkt_5a             ;exceptional frame
        ;                               ;build header for long frame
        xor    ax,ax
        cmp    cx,ARC_XMTU              ;length less than long Frame ?
        jbe    send_Long_1              ;yes, send it
        mov    cx,ARC_XMTU              ;No: send 504 bytes
send_Long_1:
        mov    ax,508                   ;number of bytes - 4 Byte header
        sub    ax,cx                    ;Offset is 508 - clientData
        mov    ah,al                    ;length in ah for stosw and long frame
        xor    al,al                    ;al=0 indicates non-normal frame
        stosw                           ;
        mov     al,ah                   ;Move offset to back to al
        xor     ah,ah                   ;
        sub     al,4                    ; -4 Byte for long frame
        add     di,ax                   ;jump over unused bytes
        jmp     send_splits
send_pkt_5a:                            ; build header for exceptional frame
        ;
        mov     ax,504                  ;Octet-Zahl - 8 Byte (long frame - 4 padding bytes)
        sub     ax,cx                   ;Offset is 504 - clientData
        mov     ah,al                   ;
        xor     al,al                   ;
        stosw                           ;
        mov     al,ah                   ;
        xor     ah,ah                   ;
        sub     al,4                    ;
        add     di,ax                   ;
        mov     al,Protocol_ID          ;First padding byte
        mov     ah,0ffh                 ;indicates exceptional frame
        stosw                           ;
        mov     ax,0ffffh               ;Another two padding bytes
        stosw                           ;
        jmp     send_splits

        ;                               header for normal frame
send_normal_1:
        mov     ax,252                  ;
        sub     ax,cx                   ;Offset is 252 - clientData
        xor     ah,ah
        stosb                           ;store offset
        sub     al,3                    ;
        add     di,ax                   ;jump over unused bytes

Send_Splits:
        mov    al,Protocol_ID
        xor    ah,ah                    ;We hope it was only one packet
        cmp    Split_number,0
        je     Send_Transmit_split      ;Ok, it was only one
        cmp    Pkt_number,0             ;A sequence: is it the first fragment
        jne    Send_Splits_2            ;
        mov    ah,Split_Number          ;yes: Splitflag is (T-2)*2+1
        jmp    send_Transmit_split      ;and send it
Send_Splits_2:
        mov    ah,Pkt_number            ;

Send_Transmit_split:
        stosw                           ;
        mov    ax,Sequence_Number       ;
        stosw
        cmp    protocol_ID,213          ;ARP and RARP packets differ between
        je     send_arp                 ;ARCnet and Ethernet, because the hardware
        cmp    protocol_ID,214          ;type is 7 for ARCnet and 1 for Ethernet and
        je     send_ARP                 ;address length are 1 for ARCnet and 6 for Ethernet
        cld
        pop    si                       ;its and IP or IPX packet: just send it
        pop    ds                       ;ds:si for movsw
        assume ds:nothing
        push   cx
        call   movemem                  ;und nun noch cx bytes clientdata ｜ertragen   ****
        pop    cx
        push   ds                       ;the next fragment
        push   si
        mov    ax,cs
        mov    ds,ax
        assume ds:code
        jmp    send_transmit
Send_ARP:
        pop    si
        pop    ds
        assume ds:nothing
        mov    ax,0700h                 ;HardwareType ARCnet
        stosw
        mov    ax,0008h                 ;Protocol is IP
        stosw
        mov    ax,0401h                 ;Hardware and IP-Length
        stosw
        add    si,6
        movsw                           ;move opcode
        add    si,5
        movsb                           ;sender hardware adress
        movsw                           ;sender IP Adress
        movsw
        push   cx
        mov    cx,3
Send_ARP_loop:                          ;check ARP destination for broadcast
        lodsw
        cmp    ax,0ffffh
        je     Send_ARP_1
        loop   Send_ARP_loop
        xor    al,al
        jmp    Send_ARP_2
send_ARP_1:
        dec    cx
        rep    lodsw
        mov    al,ah
send_ARP_2:
        stosb

        movsw                   ;destination IP address
        movsw
        pop    cx               ;cx was on the stack
        push   ds
        push   si
        mov    ax,cs
        mov    ds,ax
        assume ds:code
send_transmit:                  ;now send the packet

	mov	al,ENBL_XMT or (XMTPAGE shl 3)
	loadport
	setport	COMMAND
	out	dx,al
        mov     al,pkt_number
        cmp     al,split_number                   ;was it the last fragment
        jae     send_ende                         ;yes: sending is done
        add     pkt_number,2
        mov     cx,Stored_CX
        sub     cx,ARC_XMTU                       ;No: we did send a long packet
        jmp     New_Send_2
send_ende:
        pop     si
        pop     ds
        assume  ds:nothing
	clc
	ret


;Set address on controller
	public	set_address
set_address:
	assume	ds:nothing
;enter with ds:si -> address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
	mov	dh,CANT_SET
	stc
	ret


rcv_mode_3:
;receive mode 3 is the only one we support, so we don't have to do anything.
	ret


	public	set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, ax = number of addresses,
;  cx = number of bytes.
;return nc if we set all of them, or cy,dh=error if we didn't.
	mov	dh,NO_MULTICAST
	stc
	ret


	public	terminate
terminate:
	assume	ds:code
	loadport
	setport	IMASK
	mov	al,0
	out	dx,al

        loadport
	setport COMMAND
	mov	al,DSBL_RCV
	out	dx,al
	mov	al,DSBL_XMT
	out	dx,al

        loadport
	setport	STATUS			;do we need to do this [rnn]?
	in	al,dx

	ret


	public	reset_interface
reset_interface:
;reset the interface.
;we don't do anything.
	ret


	include	timeout.asm

;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type, dl = packet class.
	extrn	recv_find: near

;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
	extrn	recv_copy: near

;call this routine to schedule a subroutine that gets run after the
;recv_isr.  This is done by stuffing routine's address in place
;of the recv_isr iret's address.  This routine should push the flags when it
;is entered, and should jump to recv_exiting_exit to leave.
;enter with ax = address of routine to run.
	extrn	schedule_exiting: near

;recv_exiting jumps here to exit, after pushing the flags.
	extrn	recv_exiting_exit: near

	extrn	count_in_err: near
	extrn	count_out_err: near

	public	recv
recv:
;called from the recv isr.  All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
	assume	ds:code

recv_1:
        loadport			;get the status to see if we got
	setport	STATUS			;a false alarm.
	in	al,dx
        test	al,ST_RI
        jnz     recv_1a                 ;its a packet, don't exit
        ret
recv_1a:
        mov     es,mem_base             ;access the card via es:bx
	mov	bx,RCVPAGE * 512

;get packet length and splitflag
        mov     ax,es:[bx+2]            ;look for offset
        mov     cx,252                  ;-4 for client data
        cmp     al,0                    ;test for non-normal frame
        jne     short recv_my_1
        mov     cx,508
        mov     al,ah
recv_my_1:
        xor     ah,ah
        add     bx,ax                 ;jump to packet
        sub     cx,ax
        mov     ax,es:[bx]
        cmp     ah,0ffh               ;test for exceptional frame
        jne     short recv_my_2
        add     bx,4
        sub     cx,4
        mov     ax,es:[bx]            ;get the real splitflag
recv_my_2:
        test    ah,1
        jz      recv_more             ;with the first fragment of a packet we
                                      ;must be very fast because Novell
                                      ;transmits fast, so we don't jump
        cmp     ah,(MAX_FRAGMENTS - 2)*2+1
        ja      recv_reset
recv_new:
        mov     last_expected,ah      ;in a new sequence the splitflag is
                                      ;last splitflag -1 or zero for one packet
        mov     recv_protocolbyte,al  ;protocol for tests
        mov     di,es:[bx+2]
        mov     expected_sequence,di
        mov     recv_offset,offset recv_buffer
        xor     ah,ah                 ;zero it because otherwise we would
                                      ;think the sequence is complete with the
                                      ;first packet
recv_my_5:
        cmp    ah,last_expected       ;was it the last fragment?
        jae    recv_to_application    ;Never store the last fragment
        push   ax
        mov    ds,mem_base            ;move packet to the buffer
        assume ds:nothing
        mov    si,bx
        add    si,4
        mov    bx,cs
        mov    es,bx
        mov    di,cs:recv_offset

        cld
        call   movemem
        mov    ax,cs
        mov    ds,ax
        assume ds:code
        pop    ax
        mov    recv_offset,di
        mov    expected_packet,ah
        add    expected_packet,2        ;the next packet to expect
recv_isr_9:

	loadport			;enable reception again.
	setport	COMMAND
	mov	al,ENBL_RCV or (RCVPAGE shl 3) or BCAST
	out	dx,al

	jmp	recv_1
recv_reset:
        mov     last_expected,0feh      ;If a in-sequenve packet tests not ok
        jmp     recv_isr_9

recv_more:
        cmp     ah,0
        je      recv_new                 ;splitflag zero is a new packet
        cmp     last_expected,0feh       ;Are we in a sequence?
        je      recv_isr_9               ;No
        cmp     ah,expected_packet       ;Test splitflag,protocolbyte and sequence number
        jb      recv_isr_9               ;Its a replayed (or wrong) packet
        ja      recv_reset               ;Packet out of order
        cmp     recv_protocolbyte,al
        jne     recv_reset
        mov     di,es:[bx+2]
        cmp     di,expected_sequence
        jne     recv_reset
        cmp     ah,(MAX_FRAGMENTS - 1)*2    ;we can only store MAX_FRAGMENT
        jbe     recv_My_5
        jmp     recv_reset

recv_to_application:                          ;we gathered the whole sequence
                                              ;and have now (hopefully) enough
                                              ;time to build the EtherNet Packet
        add     bx,4
        mov     recv_packet,bx                ;offset of client data for the last fragment
        mov     last_length,cx                ;length of client data of the last fragment
        mov     ax,ds
        mov     es,ax
        mov     cx,recv_offset
        sub     cx,offset recv_buffer         ;length of reassembled part

        mov     last_expected,0feh            ;the sequence is complete
        add     cx,14                         ;for the EtherNet-Header
        add     cx,last_length                ;and the last fragment

        mov     al,recv_protocolbyte
        mov     dl,driver_class
        cmp     al,250                  ;is it IPX
        jne     recv_new_4              ;No
        mov     recv_protocol,3781h
        test    OPTION_8023,N_OPTION    ;Is 802.3-Option set ?
        jz      recv_new_10             ;No, proceed as normal
        mov     dl,11                   ;Yes, now we are driver class 11
        mov     recv_protocol,0ffffh    ;and use novell protocol ffffh
        jmp     recv_new_10

recv_isr_9a:
        jmp     recv_isr_9

recv_new_4:
        cmp     al,212
        jne     recv_new_5
        mov     recv_protocol,0008h     ;IP packet
        jmp     recv_new_10
recv_new_5:
        cmp     al,213
        jne     recv_new_6
        mov     recv_protocol,0608h     ;ARP Packet
        mov     cx,42
        jmp     recv_new_10
recv_new_6:
        cmp     al,214
        jne     recv_new_7
        mov     recv_protocol,3580h     ;RARP Packet
        mov     cx,42
        jmp     recv_new_10

recv_new_7:
	cmp	al,251
	jne	recv_new_8
        mov     recv_protocol,0CAACh     ;LANSoft
        jmp     recv_new_10

recv_new_8:
        ;mov     ah,al
        ;mov     recv_protocol,ax        ;We dont know the frame and
                                         ;double te protocol ID for inspection of
                                         ;these packets by a watch client
        jmp      recv_isr_9              ;to be comaptible with other drivers,
                                         ;we just free the frame
recv_new_10:
        push    cx
        mov     ax,ds
        mov     es,ax
        mov     di,offset recv_protocol
        call    recv_find                ;find a client who wants this packet
        pop     cx

        mov     ax,es                        ;is this pointer null?
        or      ax,di

        je      recv_isr_9a                  ;Yes, forget the packet

        push    cx                           ;remember length and buffer address
        push    es
        push    di

        mov     si,RCVPAGE*512               ;sid and did of the last fragment
        mov     ds,mem_base
        assume  ds:nothing
        lodsw
        push    cx
        push    ax
        mov     bx,cs
        mov     ds,bx
        assume  ds:code

        cmp     ah,0                            ;receiving broadcast
        jne     recv_directed
        mov     cx,3
        mov     ax,0ffffh
        rep     stosw
        jmp     short recv_source
recv_directed:                                  ;directed packets
        xor     ax,ax
        mov     cx,2
        rep     stosw
        pop     ax
        push    ax
        xor     al,al
        stosw
recv_source:                                    ;source address
        xor     ax,ax
        mov     cx,2
        rep     stosw
        pop     ax
        mov     ah,al
        xor     al,al
        stosw
        mov     ax,recv_protocol                ;Ethernet protocol
        stosw

        mov     si,offset recv_buffer           ;with ARP and RARP its
        cmp     recv_protocol,0608h             ;its just the opposite as in
        je      recv_arp                        ;case of sending packets
        cmp     recv_protocol,3580h
        je      recv_arp
        pop     cx
        sub     cx,14
        sub     cx,last_length
        cld
        jcxz    recv_move_last
        call    movemem                        ;The first fragments
recv_move_last:
        mov     cx,last_length
        mov     si,recv_packet
        mov     ds,mem_base
        assume  ds:nothing
        cld
        call    movemem                        ;And the last fragment
        jmp     recv_copied
recv_arp:
        pop     cx
        mov     si,recv_packet
        mov     ds,mem_base
        assume  ds:nothing
        cld
        xor     al,al
        mov     ah,1
        stosw                         ;Put Hardware-Type EtherNet at ist place
        mov     ax,0008h
        stosw                         ;Protocol used is IP
        mov     ax,0406h              ;Length of Ethernet and IP-Adress
        stosw
                                      ;now we transfer ARCnet-Client-Data to Ethernet-Client-Data
        add     si,6
        movsw                         ;move opcode
        mov     cx,5
        xor     al,al                 ;Padd first five byte of source address with zero
        rep     stosb
        movsb                         ;Move ARCNet-Hardware Adress
        movsw                         ;Sender IP-Adress
        movsw
        lodsb                         ;now we check destination for Broadcast
        cmp     al,0
        je      recv_hw_bcast
        mov     ah,al                 ;directed ARP
        xor     al,al
        mov     cx,5
        rep     stosb
        mov     al,ah
        stosb
        jmp     recv_client_2
recv_hw_bcast:
        mov    ax,0ffffh              ;Target was Broadcast
        mov    cx,3
        rep    stosw
recv_client_2:
        movsw                         ;Target IP-Address
        movsw
recv_copied:
        cmp     cs:recv_protocol,0ffffh  ;Novell 802.3 packet ?
        jne     recv_copied_2

        pop     di                    ;Yes: put length in prot field
        push    di
	mov	ax,es:[di+16]   	; get len
	xchg	ah,al
	inc	ax			; make even (rounding up)
	and	al,0feh
	xchg	ah,al
	mov	es:[di+12],ax   	; save in prot field

recv_copied_2:

        pop     si
        pop     ds
        pop     cx
        assume  ds:nothing
        call    recv_copy               ;tell the client that we
        mov     ax,cs                   ;copied the packet
        mov     ds,ax
        assume  ds:code
        jmp     recv_isr_9

	public	timer_isr
timer_isr:
;if the first instruction is an iret, then the timer is not hooked
	iret

;any code after this will not be kept.  Buffers used by the program, if any,
;are allocated from the memory between end_resident and end_free_mem.
	public end_resident,end_free_mem
end_resident	label	byte
end_free_mem	label	byte

	public	usage_msg
usage_msg	db	"usage: arcether [options] <packet_int_no> <hardware_irq> <io_addr> <mem_base>",CR,LF,'$'

	public	copyright_msg
copyright_msg	db	"Packet driver for Novell ARCnet TCP/IP and IPX version ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,CR,LF
		db	"Portions Copyright 1988 Philip Prindeville",CR,LF
                db      "Copyright 1991 Martin Wilmes",CR,LF,'$'

no_arcnet_msg	db	"No ARCnet found at that address.",CR,LF,'$'
failed_test_msg	db	"Failed self test.",CR,LF,'$'

int_no_name	db	"Interrupt number ",'$'
io_addr_name	db	"I/O port ",'$'
mem_base_name	db	"Memory address ",'$'

	extrn	set_recv_isr: near

;enter with si -> argument string, di -> word to store.
;if there is no number, don't change the number.
	extrn	get_number: near

;enter with dx -> name of word, di -> dword to print.
	extrn	print_number: near
        extrn   flagbyte

;=length of our address.
	extrn	address_len: word

;-> the assigned address of the card.
	extrn	rom_address: byte

	public	parse_args
parse_args:
;exit with nc if all went well, cy otherwise.
        test    cs:flagbyte,N_OPTION            ;Check if -n Option was used
        jz      next_arg
        xor     cs:flagbyte,N_OPTION            ;if yes: clear this flag and
        or      OPTION_8023,N_OPTION            ;set our own flag, because
        mov     cs:[driver_class+1],11          ;standard N_OPTION does not
                                                ;work with a ARCnet-Driver
                                                ;and has another meaning for us
next_arg:
	mov	di,offset int_no
                                                
                                                
	call	get_number
	mov	di,offset io_addr
	call	get_number
	mov	di,offset mem_base
	call	get_number
	clc
	ret


no_arcnet_error:
	mov	dx,offset no_arcnet_msg
	stc
	ret
failed_test_error:
	mov	dx,offset failed_test_msg
error:
	stc
	ret


	public	etopen
etopen:
;reset the board via the I/O reset port, then wait for it to become sane again.

	mov	ax,mem_base		;test the memory first.
	mov	cx,2048
	call	memory_test
	jne	no_arcnet_error

	mov	es,mem_base

	loadport
	setport SW_RST
	in	al,dx

	mov	ax,RST_IVAL
	call	set_timeout
etopen_1:
	call	do_timeout
	jne	etopen_1

        loadport
	setport	STATUS
	in	al,dx

;since we've just reset:
;	reset the POR flag,
;	check the diagnostic byte in the buffer,
;	grab the node ID, and assign it to the host number.

	test	al,ST_POR
	je	etopen_2

        loadport
	setport	COMMAND
	mov	al,CLR_FLGS or FL_POR or FL_RECON
	out	dx,al

	mov	al,es:[0]
	cmp	byte ptr es:[0],TSTWRD
	je	etopen_3
	jmp	failed_test_error	;failed power on self-test.
etopen_3:
	mov	al,es:[1]
	mov	rom_address[5],al
	mov	address_len,ARCADDR_LEN
etopen_2:

;another simple diagnostic:
;	force test flag on in RIM,
;	check to see that it is set,
;	reset it.

	loadport
	setport	COMMAND
	mov	al,LD_TST_FLG or FL_TST
	out	dx,al

        loadport
	setport STATUS
	in	al,dx

	test	al,FL_TST
	jne	etopen_4
	jmp	failed_test_error	;failed forced self-test.
etopen_4:
        loadport
	setport	COMMAND
	mov	al,LD_TST_FLG
	out	dx,al
        loadport
	setport STATUS
	in	al,dx

	pushf
	cli

	call	set_recv_isr

;now we enable the board to interrupt
;us on packet received.  Not transmiter available
;(i.e. transmission complete).  We don't have
;any control over POR, since it is NMI...
;RECON seems useless.

	loadport
	setport	IMASK
	mov	al,ST_RI
	out	dx,al

	; we should allow extended packets
        loadport
	setport	COMMAND
	mov	al,DFN_CONF or CONF_XTND
	out	dx,al

	mov	al,ENBL_RCV or (RCVPAGE shl 3) or BCAST;
	out	dx,al

	popf

	mov	al, int_no		; Get board's interrupt vector
	add	al, 8
	cmp	al, 8+8			; Is it a slave 8259 interrupt?
	jb	set_int_num		; No.
	add	al, 70h - 8 - 8		; Map it to the real interrupt.
set_int_num:
	xor	ah, ah			; Clear high byte
	mov	int_num, ax		; Set parameter_list int num.

	clc
	ret

	public	print_parameters
print_parameters:
	mov	di,offset int_no
	mov	dx,offset int_no_name
	call	print_number
	mov	di,offset io_addr
	mov	dx,offset io_addr_name
	call	print_number
	mov	di,offset mem_base
	mov	dx,offset mem_base_name
	call	print_number
	ret

	include	memtest.asm

code	ends

	end
