; ===========================================================================
; Ne2000 EtherNet Equates for NIC registers
; ===========================================================================
ne2000card      db 0
ne2000found     db 'NE2000',0

NE2000BASE              dw  300h ; equ 300h
PAGESTART               equ NE2000BASE+1
PAGESTOP                equ NE2000BASE+2
BOUNDARY                equ NE2000BASE+3
TRANSMITSTATUS          equ NE2000BASE+4
TRANSMITPAGE            equ NE2000BASE+4
TRANSMITBYTECOUNT0      equ NE2000BASE+5
NCR                     equ NE2000BASE+5
TRANSMITBYTECOUNT1      equ NE2000BASE+6
INTERRUPTSTATUS         equ NE2000BASE+7
CURRENT                 equ NE2000BASE+7         ; in page 1
REMOTESTARTADDRESS0     equ NE2000BASE+8
CRDMA0                  equ NE2000BASE+8
REMOTESTARTADDRESS1     equ NE2000BASE+9
CRDMA1                  equ NE2000BASE+9
REMOTEBYTECOUNT0        equ NE2000BASE+0ah
REMOTEBYTECOUNT1        equ NE2000BASE+0bh
RECEIVESTATUS           equ NE2000BASE+0ch
RECEIVECONFIGURATION    equ NE2000BASE+0ch
TRANSMITCONFIGURATION   equ NE2000BASE+0dh
FAE_TALLY               equ NE2000BASE+0dh
DATACONFIGURATION       equ NE2000BASE+0eh
CRC_TALLY               equ NE2000BASE+0eh
INTERRUPTMASK           equ NE2000BASE+0fh
MISS_PKT_TALLY          equ NE2000BASE+0fh
IOPORT                  equ NE2000BASE+10h

PSTART                  equ 46h
PSTOP                   equ 80h

_rcr                    db 0      ; Recv. Config. reg
_tcr                    db 0      ; Trans. Config. reg
_dcr                    db 58h    ; data config. reg
_imr                    db 0bh    ; intr. mask reg
__imr                   db 1bh    ; image intr. mask reg
rbyte_count             dw ?
sbyte_count             dw ?
badpkt                  dw 0
TRANSMITBUFFER          equ 40h

ConfigRegA EQU 10h
ConfigRegB EQU 11h
; ===========================================================================
Init_NE2000  Proc far
; ===========================================================================
	xor bx,bx
        mov cx, 7
I_NE2_loop:
        mov dx, word ptr cs:NE2_IO_Table[bx]
        cmp dx, 0
        je  I_NE2_next
;
        mov cs:IoBase, dx
        outb 7, -1
	inb ConfigRegA
	mov ah,al
	and al,7
	add al,al
	cmp al,bl
        jne I_NE2_next
	and ah,NOT 80h
	outb ConfigRegA, ah
	inb ConfigRegA
	cmp al,ah
        je I_NE2_ok

I_NE2_next:
	add bx,2
        loop I_NE2_loop
	stc
        ret

I_NE2_ok:
        mov  dx, cs:IoBase
        mov  cs:NE2000BASE, dx
        clc

I_NE2_done:
	ret
NE2_IO_Table:
        DW 300h
        DW 240h
        DW 280h
        DW 2C0h
        DW 320h
        DW 340h
        DW 360h,0
NE2_Irq_Table:
        DB 3
        DB 4
        DB 5
        DB 9
        DB 10
        DB 11
        DB 12
        DB 15
Init_NE2000  Endp

; ===========================================================================
PCtoNIC proc far
; ds:si = packet to be transferred
;    cx = byte count
;    ax = NIC Buffer page to transfer to
; ===========================================================================
        push ax
        inc  cx         ; make even
        and  cx, 0fffeh ; 1111-1111-1111-1110 copy all hi, ignore lo bit 1
        mov  dx, REMOTEBYTECOUNT0
        mov  al, cl
        out  dx, al
        mov  dx, REMOTEBYTECOUNT1
        mov  al, ch
        out  dx, al
        pop  ax         ; get our page back
        mov  dx, REMOTESTARTADDRESS0
        out  dx, al
        mov  dx, REMOTESTARTADDRESS1
        mov  al, ah
        out  dx, al
        mov  dx, NE2000BASE
        mov  ah, 12h  ; write and start
        out  dx, al
        mov  dx, IOPORT
        shr  cx, 1     ; loop half as many times becuase of double byte xfer
_pctonic1:
        lodsw
        out  dx, ax
        loop _pctonic1
        mov  cx, 0
        mov  dx, INTERRUPTSTATUS
_pctonicchkdma:
        in   al, dx
        test al, 40h ; DMA finished?
        jnz  _toNICEND
        jmp  _pctonicchkdma
_toNICEND:
        mov  dx, INTERRUPTSTATUS
        mov  al, 40h ; Clear DMA interrupt bit in ISR
        out  dx, al
        clc
        ret
PCtoNIC endp

; ===========================================================================
NICtoPC proc far
; es:di = packet to be transmitted
;    cx = size of packet
;    ax = NIC Buffer page to transfer from 
; ===========================================================================
        push ax
        inc cx
        and cx, 0fffeh
        mov dx, REMOTEBYTECOUNT0
        mov al, cl
        out dx, al
        mov dx, REMOTEBYTECOUNT1
        mov al, ch
        out dx, al
        pop ax      ; Get our page back
        mov dx, REMOTESTARTADDRESS0
        out dx, al
        mov dx, REMOTESTARTADDRESS1
        mov al, ah
        out dx, al
        mov dx, NE2000BASE
        mov al, 0ah               ; Read and start
        out dx, al
        mov dx, IOPORT
        shr cx, 1
_ntopww:
        in  ax, dx
        stosw        ; es:di
        loop _ntopww
        mov dx, INTERRUPTSTATUS
_ntopcdma:
        in  al, dx
        test al, 40h
        jnz  _ntopwe
        jmp  _ntopcdma
_ntopwe:
        out dx, al  ; clear RDMA bit in NIC ISR
        ret
NICtoPC endp

; ===========================================================================
dp8390_initialize proc far
; Initializes the NIC for a typical network system.
; Receive Buffer Ring = 2600h to 4000h
; Transmit Buffer     = 2000h to 2600h
; Entry: none
; ===========================================================================
        mov al, 21h ; stop mode
        mov dx, NE2000BASE
        out dx, al
        mov al, _dcr
        mov dx, DATACONFIGURATION
        out dx, al
        mov dx, REMOTEBYTECOUNT0
        xor al, al
        out dx, al
        mov dx, REMOTEBYTECOUNT1
        out dx, al
        mov al, _rcr
        mov dx, RECEIVECONFIGURATION
        out dx, al
        mov al, 20h
        mov dx, TRANSMITPAGE
        out dx, al
        mov al, 02
        mov dx, TRANSMITCONFIGURATION
        out dx, al  ; temporarily go into loopback mode
        mov al, 26h
        mov dx, PAGESTART
        out dx, al
        mov dx, BOUNDARY
        out dx, al
        mov al, 40h
        mov dx, PAGESTOP
        out dx, al
        mov al, 61h
        mov dx, NE2000BASE               ;   page 1 regs
        out dx, al
        mov al, 26h
        mov dx, CURRENT
        out dx, al
        mov al, 22h ; page 0 start mode
        mov dx, NE2000BASE
        out dx, al
        mov al, 0ffh
        mov dx, INTERRUPTSTATUS
        out dx, al
        mov al, _imr
        mov dx, INTERRUPTMASK
        out dx, al
        mov dx, TRANSMITCONFIGURATION
        mov al, _tcr
        out dx, al ; TCR in normal mode, NIC is now ready for reception
        ret
dp8390_initialize endp

; ===========================================================================
dp8390_send proc far
; ===========================================================================
        cli
        ; DP8390/901/902/905 ready to transmit?
        ; no, queue packet in PC memory
        mov dx, NE2000BASE
        in  al, dx      ; read NIC NE2000BASE reg
        cmp al, 26h     ; transmitting?
        je  _dpqueueit
        ; program NIC to transmit packet (NE2000BASE Reg = 26h)
        push cx
        mov ah, TRANSMITBUFFER
        xor al, al
        Call PCtoNIC ; xfer from PC ram to NIC ram
        mov dx, TRANSMITPAGE
        mov al, TRANSMITBUFFER
        out dx, al
        pop cx  ; get our byte count back
        mov dx, TRANSMITBYTECOUNT0
        mov al, cl
        out dx, al
        mov dx, TRANSMITBYTECOUNT1
        mov al, ch
        out dx, al
        mov dx, NE2000BASE
        mov al, 26h
        out dx, al         ; issue transmit NE2000BASE reg
        jmp _dbfinished
_dpqueueit:
        ; yes, DMA packet from PC to local memory
        ; call queue_packet
_dbfinished:
        sti
        ret
dp8390_send endp

; ===========================================================================
_ntisrprrt proc far 
; ===========================================================================
_ntisrprrt1:
        ; Packet Receive Routine (pck_recv_rt) - clears out all good packets
        ; in local receive buffer ring. Bad packets are ignored.
        mov  dx, INTERRUPTSTATUS
        in   al, dx
        test al, 10h ; Overflow in the ring?
        jnz  ring_ovf1
        mov  al, 1
        out  dx, al ; reset PRX bit in ISR
        mov  ax, next_pkt
        mov  cx, pktlen
 ;       les  di, receivebuffer
 ;       mov es, seg receivebuffer
 ;       mov di, offset receivebuffer
        call NICtoPC
        ; Inform upper layer software of a received packet to be processed
        jmp  _ntisrprrtx
_chkring:
        ; check if it is empty
        mov  dx, BOUNDARY
        in   al, dx
        mov  ah, al ; saveboundary in AH
        mov  dx, NE2000BASE
        mov  al, 62h
        out  dx, al ; switched to pg 1 of NIC
        mov  dx, CURRENT
        in   al, dx
        mov  bh, al ; Current page reg
        mov  dx, NE2000BASE
        mov  al, 22h
        out  dx, al ; back to pg 0
        cmp  ah, al ; recv buff ring empty?
        jne  _ntisrprrt1
ring_ovf1:
        ; following is required to recover from ringbuff overflow
        mov  dx, NE2000BASE
        mov  al, 21h
        out  dx, al ; Stop NIC
        mov  dx, REMOTEBYTECOUNT0
        xor  al, al
        out  dx, al
        mov  dx, REMOTEBYTECOUNT1
        out  dx, al
        mov  dx, INTERRUPTSTATUS
        mov  cx, 7fffh   ; load time out counter
_ntpwait:
        in   al, dx      ; Look for RST bit to be set if we fall through, the
        test al, 80h     ; RST bit may not get set because the NIC was 
        loop _ntpwait    ; currently transmitting
        mov  dx, TRANSMITCONFIGURATION
        mov  al, 2
        out  dx, al      ; loopback mode 1
        mov  dx, NE2000BASE
        mov  al, 22h
        out  dx, al      ; stop mode
        mov  ax, next_pkt
        mov  cx, pktlen
;        les  di, receivebuffer
        call NICtoPC
        mov  dx, INTERRUPTSTATUS
        mov  al, 10h
        out  dx, al  ; clear OFb
        mov  dx, TRANSMITCONFIGURATION
        mov  al, _tcr
        out  dx, al
        jmp  _chkring
_ntisrprrtx:
        ret
_ntisrprrt endp

; ===========================================================================
_ntisrptxrt proc far
; ===========================================================================
        ; packet transmit routine - determine statis of transmitted packet,
        ; then checks th transmit-pending queue for the next available packet. 
        mov  dx, INTERRUPTSTATUS
        mov  al, 0ah
        out  dx, al  ; reset PTX and TXE bits in ISR
        mov  dx, TRANSMITSTATUS ; check for erroreous TX
        in   al, dx
        test al, 38h ; is FU, CRS, or ABT bits set in TSR
        jnz  _bad_tx
        ; inform upper layer software of successful transx
        jmp  _chktxqueue
_bad_tx:             ; in here ifit is a bad transmission
        mov  ax, badpkt
        inc  ax
        mov  badpkt, ax
        ; inform upper layer software of errored packet
_chktxqueue:
        ; call check_queue
        cmp ax, 0                     ; NZ if packet is in queue
        je _netisrpoll                ; ds:si -> packet
        call dp8390_send
        ret
_ntisrptxrt endp

; ===========================================================================
netisr proc far
; Internet service routine
; ===========================================================================
        cli
        push ax
        push bx
        push cx
        push dx
        push di
        push si
        push ds
        push es
        push bp
        mov al, 0bch  ; 1011-1100h
        out 21h, ax   ; turn off IRQ3
        sti
        mov ax, 0070h
        mov ds, ax    ; point to kernel data/codeseg
        ; read INTERRUPT STATUS REGISTER for receive packets, transmitted packets
        ; and errored transmitted packets
_netisrpoll:
        mov  dx, INTERRUPTSTATUS
        in   al, dx
        test al, 1    ; packet recv'd?
        jz   _ntnoprrt
        call _ntisrprrt
        jmp  _netisrpoll
_ntnoprrt:
        test al, 0ah  ; packet transmitted?
        jz   short _exit_isr
        call _ntisrptxrt
        jmp  _netisrpoll
_exit_isr:   
        mov  dx, INTERRUPTMASK
        mov  al, 0
        out  dx, al
        cli
        mov  al, 0b4h ; 10110100h
        out  21h, al  ; IRQ3 turned back on
        mov  al, 63h
        out  20h, al  ; EOI for IRQ3
        sti
        mov dx, INTERRUPTMASK ; intr from NIC is enabled at this point so
        mov al, __imr         ; 8259 IC dosnt miss any IRQ edges from the NIC
        out dx, al            ; (IRQ is edge sensitive)
        push bp
        push es
        push ds
        push si
        push di
        push dx
        push cx
        push bx
        push ax
        sti
        iret
netisr endp

; ===========================================================================
dp8390p proc far
; NE2000 NIC ISR for 8390 Chipset
; ===========================================================================
        cli
        pusha
_dp8390_isr:
        ; read DP8390/901/902/905 interrupt status reg
        ; packet reception?
        je _dp8390_rp ; yes
        ; packet transmission?
        je _dp8390_sp ; yes
        jmp _dp8390_eoi
_dp8390_sp:
        ; reset PTX Bit in Interrupt status reg
        ; read transmit status reg
        ; inform high level software of good/bad transmission
        ; empty transmission queue?
        ; yes, goto _dp9390_isr or exit isr?
        ; no, transmit next packet in queue
        ; update queue pointers
        jmp _dp8390_isr
_dp8390_rp:
        ; reset PRX Bit in Interrupt status register
        ; DMA packet from local memory to PC
        ; Inform High Level software of received packet
        ; Receive Buffer Ring Empty?
        ; No. jmp _dp8390_rp
        ; yes, jmp _dp8390_isr
        jmp _dp8390_isr
_dp8390_eoi:
        ; send E0 to  8259

        popa         ; restore pc registers
        sti          ; enable interrupts
        iret
dp8390p endp
