pkt_vers equ     0      ; packet version


;  The driver is based on the RTL8139(A/B) Programming Guide (v0.1).
;  This packet driver is designed as a generic Realtek 8101/8136/8139 
;  ethernet controller. In no way is it supported by Realtek company 
;  so use it at your risk.
;  2009(c)  je, j_lestrada@yahoo.com

;  Copyright, 1988-1992, Russell Nelson, Crynwr Software

;   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.

	include defs.asm
	include pci.inc

cgroup group code, _text, init

code    segment para public 'code'
	assume  cs:cgroup, ds:cgroup

my_notfound     db      '**** DEVICE not FOUND!!***',CR,LF,'$'
my_msg          db      ' ** Heelo worls!!! **',CR,LF,'$'
assumed_msg     db      'No EEPROM, '
		db      ' using command line parameters.',CR,LF,'$'
above_16_meg_msg:
		db      'Buffer space has been above 16Megs',CR,LF,'$'
		db      'Memory above 16Megs is not supported by this driver.',CR,LF,'$'
scan_failed     db      'Scan I/O space was not found',CR,LF,'$'
specified_failed db      'You must specify the I/O address of the card you wish'
		db      ' to use',CR,LF,'$'
irq_problem_msg   db      'Problem reading IRQ for PCI configuration',CR,LF,'$'
my_loop         db      '  --->loop',CR,LF,'$'
my_nexterr      db      ' +++> error',CR,LF,'$'
my_nexterr1     db      ' +++> error1',CR,LF,'$'
my_nexterr2     db      ' +++> error2',CR,LF,'$'
my_nexterr3     db      ' +++> error3',CR,LF,'$'
my_nexterr4     db      ' +++> error4',CR,LF,'$'
my_nexterr5     db      ' +++> error5',CR,LF,'$'
my_nexterr6     db      ' +++> error6',CR,LF,'$'
my_nexterr7     db      ' +++> error7',CR,LF,'$'
my_nexterr8     db      ' +++> error8',CR,LF,'$'
my_ether        db      'Ethernet: ',CR,LF,'$'
my_ether2       db      'Ethernet length: ',CR,LF,'$'
TENN            db      10
; System Configuration Pointer (SCP)
SCP_STRUC                       struc
	scp_reserved0   dw      0
	scp_sysbus              dw      ?                       ;- SYSBUS Config byte
	scp_reserved1   db      4       dup (0)
	scp_iscp_add    dd      ?                       ;- ISCP Physical Address
SCP_STRUC                       ends

; Intermediate System Configuration Pointer (ISCP)
ISCP_STRUC                      struc
	iscp_busy               db      ?                       ;- Initialization Busy Flag
	iscp_reserved0  db      0
	iscp_scb_offset dw      ?                       ;- Offset Of SCB Address
	iscp_scb_lo_add dw      ?                       ;- Base Of SCB Address (A15 - A0)
	iscp_scb_hi_add dw      ?                       ;- Base Of SCB Address (A23 - A16)
	iscp_filler             db      6       dup (0)
ISCP_STRUC                      ends

; System Control Block (SCB)
SCB_STRUC       struc
	scb_status      dw      ?                       ;- SCB Status
	scb_command     dw      ?                       ;- SCB Command
	scb_cbl_add     dw      ?                       ;- CB List Offset
	scb_rfa_add     dw      ?                       ;- RFA Offset
	scb_crc_err     dd      ?                       ;- RX CRC Errors
	scb_alig_err    dd      ?                       ;- RX Unaligned Frames / CRC Errors
	scb_rsrce_err   dd      ?                       ;- RX Frames Discarded (No Resources)
	scb_dma_err     dd      ?                       ;- RX DMA Overruns
	scb_rcvd_err    dd      ?                       ;- RX Collisions Count
	scb_short_err   dd      ?                       ;- Short Frame Errors
	scb_off_timer   dw      ?                       ;- Bus Throttle T-OFF Timer
	scb_on_timer    dw      ?                       ;- Bus Throttle T-ON Timer
	scb_header_size dw      ?                       ;- Header RFD Data Size
	scb_filler              dw      ?                       ;- All Zeroes, Reserved.
SCB_STRUC               ends
; Transmit Command Block (TxCB)
TXCB_STRUC                      struc
    tx_status                   dw      ?               ; Command Block Status
    tx_command                  dw      ?               ; Command Block Command
    tx_link_ptr                 dw      ?               ; Offset To Next CB
	tx_tbd_ptr                      dw      ?               ; TBD Offset
	tx_count                        dw      ?               ; Data Bytes In TCB past header
	tx_thresh                       db      0               ; TX Threshold for FIFO Extender
	tx_filler                       db      0
TXCB_STRUC                      ends

; Received Frame Descriptor
RFD_STRUCT      struc
rfd_status      dw      ?               ; Status word for frame
rfd_eol         dw      ?               ; End of RFD list flag
rfd_link        dw      ?               ; 16 pointer to next RFD
rfd_ptr         dw      ?               ; 16 pointer to list of RBD's
rfd_cnt         dw      ?               ; Actual size of data in buffer after header
rfd_size        dw      ?               ; Size of data buffer after header
rfd_dest        db      EADDR_LEN dup(?); 48 bits of destination
rfd_source      db      EADDR_LEN dup(?); 48 bits of source
rfd_len         dw      ?                               ; Length field of frame.
RFD_STRUCT      ends

; Receive Buffer Descriptor
RBD_STRUCT      struc
rbd_status      dd      ?               ; Status word in RBD
rbd_link        dw      ?               ; 16 bit pointer to next RBD
rbd_ptr         dd      ?               ; 32 bit pointer to actual buffer
rbd_size        dw      ?               ; Size of the buffer
rbd_filler1     dw      ?               ; Not used
rbd_voffset     dw      ?               ; Virtual offset to data buffer
RBD_STRUCT      ends

;;;DESCRIPTOR      struct
;;des_comm        dd      ?       ; command/status dword
;;vlan            dw      ?       ; currently unused
;;low_buf         dd      ?       ;
;;DESCRIPTOR      ends

C_ATTENTION     macro
;;        LOAD_PORT       0x0004h          ; Issue a channel attention to the 82596
	in      al, dx
	endm
LOAD_PORT       MACRO port_param
	mov             edx, base_addr
	if port_param NE 0
		add             edx, port_param
	endif
	port_no = port_param
	ENDM

SET_PORT        MACRO port_param
	if port_param - port_no EQ 1
		inc     edx
	else
		if port_param - port_no EQ -1
			dec     edx
		else
			if port_param - port_no NE 0
				add     edx,port_param - port_no
			endif
		endif
	endif
	port_no = port_param
	ENDM

	even
virtual_mem_struct:
		dd      0
		dd      0
		dw      0
		dw      0
		dw      0
		dw      0

io_addr         dw      0,0             ; I/O address specified on the command line
no_prom         db      0       ; <> 0 if ethernet address specified on the command line
base_addr       dd      0
own_bit         dd      080000000h
eor_bit         dd      040000000h

multiple_cards          db      0               ; Set if more than one 'matching' card is found
found_one               db      0               ; = 1 if one card is found
pci_bus_dev_no          dw      ?               ; Storage for PCI bus / device number
temp_storage            dw      0, 0
temp_connector          db      01h
connector               db      0
cnvrt_to_phys_add       dw      ?
timeout         dw      ?               ;number of ticks to wait.
timeout_counter dw      ?               ;old counter zero value.
timeout_value   dw      ?
; Action command bits
EL_BIT          EQU     8000h
S_BIT           EQU     4000h
INT_BIT         EQU     2000h
IA_SETUP        EQU     0001h
CONFIG_CMD      EQU     0002h
MULTI_ADD       EQU     0003h
TRANSMIT        EQU     0004h
TDR_CMD         EQU     0005h

RU_START        EQU     0010h
CU_START        EQU     0100h

LNK_OK          EQU     8000h
TX_THRESHOLD    EQU     4h
TX_EOF_BIT              EQU     8000h
DUMP            EQU     0006h
DIAGNOSE        EQU     0007h

CU_ACTIVE       EQU     0200h
CU_STATUS_BITS  EQU     0700h

; Receive frame status in the RFD
SHORT_FRAME_BIT EQU     0080h
OVER_RUN_BIT    EQU     0100h
RESOURCES_BIT   EQU 0200h
ALIGN_BIT               EQU     0400h
CRC_BIT                 EQU     0800h
LENGTH_BIT              EQU 1000h
RX_OK                   EQU 2000h
RFD_PREFETCH    EQU 4000h
RFD_DONE_BIT    EQU 8000h

RBD_PREFETCH    EQU 4000h

COUNT_VALID             EQU     4000h
EOF_BIT                 EQU 8000h               ; End of frame marker for RBD

C_DONE_BIT              EQU     8000h
FLEXIBLE_MODE   EQU     0008h


RBD_COUNT               EQU             10h
RFD_COUNT               EQU             12h             ; More than RBD so that we don't run out
SIZE_ONE_DATA_BUFF      EQU     100h           

;**********************************************
; RTK8101 Symbolic offsets to registers
MAC0            EQU     0h      ; Ethernet hardware address
MAR0            EQU     8       ; Multicast filter
TxStatus        EQU     010h    ; Transmit status (4-32bit registers)
TxAddr          EQU     020h    ; Tx Descriptors  (4-32bit registers)
RxBuf           EQU     030h    ;
RxEarlyCnt      EQU     034h    ;
RxEarlyStatus   EQU     036h    ;
ChipCmd         EQU     037h    ; Command Register
RxBufPtr        EQU     038h    ; Current address of packet read
RxBufAddr       EQU     03Ah    ; Current buffer address
IntrMask        EQU     03Ch    ;
IntrStatus      EQU     03Eh    ;
TxConfig        EQU     040h    ;
RxConfig        EQU     044h    ;
Timer           EQU     048h    ; Genereal purpose counter
RxMissed        EQU     04Ch    ; 24-bit valid, write clears
Cfg9346         EQU     050h    
Config0         EQU     051h
Config1         EQU     052h
TimerIntrReg    EQU     054h    ; intr if gp counter reaches this value
MediaStatus     EQU     058h    
Config3         EQU     059h
MultiIntr       EQU     05Ch
RevisionID      EQU     05Eh    ; Revision of the RTL8101 chip
TxSummary       EQU     060h

RxCnt           EQU     072h    ; Packet received counter
CSCR            EQU     074h    ; Chip status and configuration register
;**********************************************

;a few useful Ethernet definitions.
;RUNT           equ     60              ;smallest legal size packet, no fcs
;GIANT          equ     1514            ;largest legal size packet, no fcs
;EADDR_LEN      equ     6               ;Ethernet address length.
;ARCADDR_LEN    equ     1

	public tx_buff_no, buff_ptrs
tx_buff_no              dw      ?
old_buff_no             dw      ?
buff_ptrs               dw      offset cgroup:cmd_buff1
			dw      offset cgroup:cmd_buff2
				
	even
align   64
	public scb_buff
scb_buff        db      size SCB_STRUC dup(0)

align   4
	public cmd_buff1, cmd_buff2
cmd_buff1               db      GIANT + 10h dup (?)
	align   4
cmd_buff2               db      GIANT + 10h dup (?)


	public  int_no
int_no  db      0,0,0,0                 ;must be four bytes long for get_number.

	public  driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class    db      BLUEBOOK,0      ;null terminated list of classes.
driver_type     db      0               ;assigned by FTP Software, <jbvb@ftp.com>
driver_name     db      'skeleton',0    ;name of the driver.
driver_function db      2
parameter_list  label   byte
	db      1       ;major rev of packet driver specification
	db      9       ;minor rev of packet driver specification
	db      14      ;length of parameter list
	db      EADDR_LEN       ;length of MAC-layer address
	dw      GIANT   ;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               ;there is no mode zero.
		dw      0,0,rcv_mode_3


	public  first_rfd, last_rfd, first_rbd, last_rbd
first_rfd               dw      ?
last_rfd                dw      ?
first_rbd               dw      ?
last_rbd                dw      ?

	public  start_rfd, start_rbd, start_rx_buff
	align   4
start_rfd               db RFD_COUNT * (size RFD_STRUCT) dup (?)
	align   4
start_rbd               db RBD_COUNT * (size RBD_STRUCT) dup (?)
	align   4
start_rx_buff   db RBD_COUNT * SIZE_ONE_DATA_BUFF dup (?)


;       public  temp_buffer
	ALIGN   16
temp_buffer     db      80 dup (0)              ; TX buffer for media detect

	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
	

send_problem:
      to_scrn 7,0,'P'
	mov             dh, CANT_SEND           ; Indicate an error.
	stc
	ret

send_pkt_toobig:
	mov     dh,NO_SPACE
	stc
	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.
;if we're a high-performance driver, es:di -> upcall.
;exit with nc if ok, or else cy if error, dh set to error number.
	assume  ds:nothing
	.386
	cmp             cx,GIANT                        ; Is this packet too large?
	ja              send_pkt_toobig

	cmp             cx, RUNT                        ; Minimum length for Ether
	jae             short oklen
	mov             cx, RUNT                        ; Make sure size at least RUNT
oklen:
;;        to_scrn 7,0,'T'
	mov             ax, cs
	mov             es, ax

	call    get_buffer
	to_scrn 7,0,'&'
	jc              send_problem
	mov             bx, di                          ; Save the start of the buffer
	mov             eax, (EL_BIT + S_BIT + TRANSMIT)*10000h
	pusha
	to_scrn 7,0,'7'
	popa
	stosd                                           ; Store the action command word + status word
;;if      CHIP EQ INTEL_82596
	mov             ax, -1
	stosw                                           ; Write the address of the next command
	stosw                                           ; Store the TBD array null pointer
;;else
;;        mov             eax, -1                         ; Write the address of the next command
;;        stosd                                           ; Store the link address - next command
;;        stosd                                           ; Store the TBD array null pointer
;;endif
;;if      CHIP EQ INTEL_82596
	mov             eax, TX_EOF_BIT
;;else
;;        mov             eax, TX_THRESHOLD*10000h + TX_EOF_BIT
;;endif
	or              ax, cx                          ; Include the length of the packet
	stosd
								; Memory copy routine
	mov             ax, cx                          ; Save byte count
	sar             cx, 2                           ; Convert to a DWORD count
	rep             movsd                           ; Copy DWORDS
	mov             cx, ax                          ; Recover byte count
	and             cx, 0003h                       ; Obtain count mod 4
	jz              short no_more
	rep             movsb                           ; Copy remaining bytes
no_more:

	mov             di, bx                          ; Recover the start of the buffer

;;if     CHIP EQ INTEL_82596
       pusha
       to_scrn 7,0,'O'
       popa
	call    issue_cmd
       to_scrn 7,0,'X'

	jc      send_problem
;;else
;;       LOAD_PORT       SCB_GEN_PTR                     ; Get to the general pointer port
;;       xor             eax, eax
;;       mov             ax, di
;;       out             dx, eax                                 ; Issue the address - upper word is low
;;       sub             dx, SCB_GEN_PTR - SCB_CMD       ; Get back to cmd port
;;       mov             al, CU_START

;;       LOAD_PORT       SCB_STATUS
;;       in              ax, dx

;;       LOAD_PORT       SCB_CMD
;;       mov             ax, CU_START
;;       or              ax, 100h
;;       out             dx, ax                                  ; Issue the command

;;       LOAD_PORT       SCB_STATUS
;;       in              ax, dx

;;       LOAD_PORT       SCB_STATUS
;;       in              ax, dx

;endif
	clc
	ret


	public  set_address
set_address:
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
	assume  ds:nothing
	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:
	ret


	public  reset_interface
reset_interface:
;reset the interface.
	assume  ds:code
	ret


;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, ds=cs,
;our interrupt has been acknowledged, and our interrupts have been
;masked at the interrupt controller.
	assume  ds:code
	ret


	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: skeleton [options] <packet_int_no>",CR,LF,'$'

	public  copyright_msg
copyright_msg   db      "Packet driver for a <foo> device, version ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+pkt_vers,CR,LF
		db      "Portions Copyright 19xx, <your name here>",CR,LF,'$'

;called when you're ready to receive interrupts.
	extrn   set_recv_isr: near

;enter with si -> argument string, di -> dword to store.
;if there is no number, don't change the number.
	extrn   get_number: near

;enter with dx -> argument string, di -> dword to print.
	extrn   print_number: near

;-> the unique Ethernet address of the card.  Filled in by the etopen routine.
	extrn   rom_address: byte

;-> current address.  Normally the same as rom_address, unless changed
;by the set_address() call.
	extrn   my_address: byte

;parse_args is called with si -> first parameter (CR if none).
	public  parse_args
parse_args:
;exit with nc if all went well, cy otherwise.
	clc
	ret


	public  etopen
etopen:
;initialize the driver.  Fill in rom_address with the assigned address of
;the board.  Exit with nc if all went well, or cy, dx -> $ terminated error msg.
;if all is okay,
	assume  ds:code
	
	.386                                    ; Must have >= 386
	call    check_virtual_mem_services
	call    etopen_chip
	jc      short exit_etopen
	mov     al, int_no              ; Get board's interrupt vector
	add     al, 8
	cmp     al, 8+8                 ; Is it a slave 8259 interrupt?
	jb      short 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.
	pusha
	lea     dx,my_nexterr8
	mov     ah,09h
	int     021h
	popa

	call    setup_receive   ; Setup receive buffers & start reception
	pusha
	lea     dx,my_nexterr2
	mov     ah,09h
	int     021h
	popa
	call    set_recv_isr
	pusha
	lea     dx,my_nexterr4
	mov     ah,09h
	int     021h
	popa
;;        call    enable_board_int
	clc
exit_etopen:
;       clc
;       ret
;if we got an error,
;       stc
	ret


check_virtual_mem_services      PROC    NEAR
; Check to see if virtual DMA service is being provided.
; Bit 5 of the Bios Flags at 40:7Bh is set if virtual services present
; Sets the appropriate routine to use for conversion to physical addresses
	mov     ax, 40h
	mov     es, ax
	test    BYTE PTR es:[007bh], 20h
	jz      short no_virtual_services_found
;;        print   virtual_services        ; Announce virtual DMA services present
	mov     cnvrt_to_phys_add,offset cgroup: virtual_address_convert
	jmp     short exit_check_virtual_mem_services
no_virtual_services_found:
;;        print   no_virtual_services     ; Announce no virtual DMA services
	mov     cnvrt_to_phys_add,offset cgroup: linear_address_convert
exit_check_virtual_mem_services:
	ret
check_virtual_mem_services      ENDP


linear_address_convert  PROC    NEAR
	; Address to be converted is in es:si
	; Converted physical address is returned in eax
	xor             eax, eax
	mov             ax, es
	shl             eax, 4
	movzx   esi, si                 ; Make certain that top part of SI is clear
	add             eax, esi
	ret
linear_address_convert  ENDP


virtual_address_convert PROC    NEAR
	mov     di, offset cgroup:virtual_mem_struct
	; Lock the memory, which in turn returns the physical address in the DMA descriptor
; debug
	mov     word ptr [di], 200h
	mov     word ptr [di+2], 0
	mov     word ptr [di+4], si
	mov     word ptr [di+6], 0
	mov     [di+8], es

    mov     ax, 8103h           ; Lock DMA region
    mov     dx, 0ch                     ; No alloc, no remap
    push    ds
    pop     es
    int     4Bh
	jc              short exit_virtual_address_convert
    test        [di+0ch], 0FF000000h
	jz              short below_16_meg
;debug
	jmp             short below_16_meg
    mov     ax, 8104h           ; Unlock DMA region
    mov     dx, 0                       ; No copy
    int     4Bh
	print   above_16_meg_msg
	stc

below_16_meg:
; debug
	jmp             short no_unlock


	; Now unlock the DMA region
    mov     ax, 8104h           ; Unlock DMA region
    mov     dx, 0                       ; No copy
    int     4Bh
no_unlock:
    mov     eax, [di+0ch]
	clc
exit_virtual_address_convert:
	ret
virtual_address_convert ENDP

;*******************************************************
;*  etopen_chip:                                       *
;*******************************************************
etopen_chip     PROC    NEAR
	call    find_base                       ; Go and find the chip
	jc      exit_etopen_chip
;;        lea     dx,my_nexterr7
;;        mov     ah,09h
;;        int     021h

	;;;call    reset_chip
 ;;   call    test_chip
;;      jc      short exit_etopen_chip2
;;        lea     dx,my_nexterr2
;;        mov     ah,09h
;;        int     021h


;;      call    init_plx_chip           ; Initialize the pci interface chip
;;      jc      short exit_etopen_chip

scp_int_ok:
	call    read_ia_pci             ; Get Ethernet address from PCI Config space

;; Reseting the RTL8101L
	mov     edx,base_addr
	add     edx, 037h
	mov     ax,010h
	out     dx,ax
check:
	in      ax,dx           ;Wait till chip finishes initilization
	and     ax,010h
	jnz     short check

	popa
	lea     dx,my_nexterr5
	mov     ah,09h
	int     021h
	pusha

	jnc             short configure
;;        lea     dx,my_nexterr6
;;        mov     ah,09h
;;        int     021h

	cmp             no_prom, 0              ; Did they specify everything we need?
	je              etopen_failed     ; No - exit
;;        lea     dx,my_nexterr7
;;        mov     ah,09h
;;        int     021h
address_specified:
	print   assumed_msg                     ; Yes, tell them we're assuming they're right.
configure:
	call    setup_cmd_buffs         ; Setup buffer for commands
	lea     dx,my_nexterr8
	mov     ah,09h
	int     021h

;;      call    config_chip                     ; Configure chip

;;
;;      Unlock config registers
;;
	mov     edx,base_addr
	add     edx,050h
	mov     ax,0C0h
	out     dx,ax
;;
;;      RxConfig = RXFTH: unlimited
;;                 MXDMA: unlimeted
;;                 AAP:   set to pro. mode
;;
	mov     edx,base_addr
	add     edx,044h
	mov     ax,0E70Fh
	out     dx,ax
;;
;;      TxConfig = IFG:   normal
;;                 MXDMA: unlimited
;;
	mov     edx,base_addr
	add     edx,040h
	mov     ax,0700h
	out     dx,ax
	inc     edx
	inc     edx
	mov     ax,0300h
	out     dx,ax
;;
;;      Max rx packet size
;;
	mov     edx,base_addr
	add     edx,0DAh
	mov     ax,01FFFh
	out     dx,ax
;;
;;      Max tx packet size
;;
	mov     edx,base_addr
	add     edx,0ECh
	mov     ax,03Bh
	out     dx,ax
;;
;;      Transmit Descriptor start address
;;
	mov     edx,base_addr
	add     edx,020h
	;;mov     si,offset csgroup:start_tx_buf
	;;mov     ax,si
	out     dx,ax
;;
;;      Receive Descriptor start address
;;
	mov     edx,base_addr
	add     edx,0E4h
	mov     si, offset cgroup:start_rx_buff
	mov     ax,si
	out     dx,ax
;;                   
;;      Enable Rx/Tx in the Command register
;;
	mov     edx,base_addr
	add     edx,037h
	mov     ax,0Ch
	out     dx,ax
;;
;;      Lock config register
;;
	mov     edx,base_addr
	add     edx,050h
	mov     ax,00h
	out     dx,ax


	lea     dx,my_nexterr3
	mov     ah,09h
	int     021h

;;        jc      short exit_etopen_chip
	lea     dx,my_nexterr4
	mov     ah,09h
	int     021h

	;;call    do_select_media

exit_etopen_chip2:
	lea     dx,my_nexterr
	mov     ah,09h
	int     021h
;;        jmp     short exit_get_pci_info

exit_etopen_chip:
	clc
	ret
etopen_failed:
	stc             ; Give up.
	ret
etopen_chip     ENDP


;*******************************************************
;*  find_base:                                           *
;*******************************************************
	public  find_base
find_base       PROC    NEAR
	call    get_pci_info            ; Go and search the card
	jc      short f_b_failed        ; Jump if card not found
	mov     eax, base_addr           ; Copy the base address for the card
	mov     io_addr, ax
	ret
f_b_failed:
	cmp     io_addr, 0h             ; Has a base address been specified
	jz      short f_b_failed1       ; No - then the scan failed
	mov     dx, offset cgroup: specified_failed
	jmp     short f_b_failed2
f_b_failed1:
	mov     dx, offset cgroup: scan_failed   ; Message to announce later
f_b_failed2:
	stc                             ; Set carry and return
	ret
find_base       ENDP

.386
;*******************************************************
;*  get_pci_info:                                      *
;*******************************************************
get_pci_info    PROC    NEAR
;        mov             si,     -1
try_next_index:
;        inc             si                                              ; Start out with index # = 0
	mov             si, 0
	mov             ah, 0b1h   ;PCI_FUNCTION_ID
	mov             al, 002h   ;FIND_PCI_DEVICE
	mov             cx, 08136h ;DEVICE_ID                   ; Look for particular device
	mov             dx, 010ECh ;VENDOR_ID
	int             01ah ;PCI_BIOS_INTERRUPT
	jc             short exit_get_pci_info2 ; Not found - exit
	mov             pci_bus_dev_no, bx              ; Save bus / device number


	mov             ah, 0b1h        ;PCI_FUNCTION_ID             ; Get IO base
	mov             al, READ_PCI_CONFIG_DWORD
	mov             di, 10h                                 ; Base address register for IO info
	int             01ah            ;PCI_BIOS_INTERRUPT
	jc              short error_get_pci_info

	and             cl, 0FCh                                ; Mask lower 2 bits of IO address
	mov             base_addr,ecx

found_a_match:

no_io_add_specified:                            ; Everything has matched - card is found

first_card_found:
	call    get_irq

	mov             found_one, 01h
	jmp             short exit_get_pci_info         ;;;short try_next_index    ; Continue the search
exit_get_pci_info2:
;;        lea     dx,my_notfound
;;        mov     ah,09h
;;        int     021h
	jmp     short exit_get_pci_info
my_exit:
;;        lea     dx,my_msg
;;        mov     ah,09h
;;        int     021h
   
exit_get_pci_info:
	cmp             found_one, 01h                  ; Carry set if there is a problem
	ret
error_get_pci_info:
	stc
	jmp             short exit_get_pci_info
get_pci_info    ENDP
;*******************************************************
;*  get_irq:                                           *
;*******************************************************
get_irq PROC    NEAR
;;        mov             base_addr, ecx
	mov             ecx,base_addr
	mov             bx, pci_bus_dev_no              ; Recover the bus / device number
	mov             ah, PCI_FUNCTION_ID             ; Get IRQ number
	mov             al, READ_PCI_CONFIG_BYTE
	mov             di, 3ch                                 ; PCI register for IRQ info
	int             PCI_BIOS_INTERRUPT
	cmp             ah, PCI_SUCCESSFULL
	je              short got_irq
;;        jc              short no_jmp
;;no_jmp:
	print   irq_problem_msg
	stc
	jmp             short exit_get_irq
got_irq:
	mov             int_no, cl
	clc
exit_get_irq:
	ret
get_irq ENDP

;*******************************************************
;*  read_ia_pci:                                       *
;*******************************************************
read_ia_pci     PROC    NEAR
; For the 8101L the Ethernet address is obtained from PCI configuration

	mov             edx,base_addr
	mov             ax,00h
	in              ax,dx
	mov             BYTE PTR rom_address, al
	inc             edx              
	mov             eax,00h
	in              ax,dx
	mov             BYTE PTR rom_address+1, al

	inc             edx              
	mov             ax,00h
	in              ax,dx
	mov             BYTE PTR rom_address+2, al
	inc             edx              
	mov             ax,00h
	in              ax,dx
	mov             BYTE PTR rom_address+3, al
	inc             edx              
	mov             ax,00h
	in              ax,dx
	mov             BYTE PTR rom_address+4, al
	inc             edx              
	mov             ax,00h
	in              ax,dx
	mov             BYTE PTR rom_address+5, al

;;        mov             di,offset cgroup:rom_address
;;        lea             dx,my_ether
;;        call            print_number
;;        lea             dx,my_ether
;;        mov             di,offset cgroup:rom_address
;;        inc             di
;;        call            print_number
;;        lea             dx,my_ether
;;        mov             di,offset cgroup:rom_address
;;        inc             di
;;        inc             di
;;        call            print_number

;;        lea             dx,my_ether2
;;        mov             ah,09h
;;        int             021h

;;        .386
;;-----------
;;        mov           ax,offset cgroup:EADDR_LEN
;;        div             TENN
;;        or              ax,03030h
;;        mov             bx,ax
;;        mov             ah,02h
;;        mov             dl,bl
;;        int             21h
;;        mov             ah,02h
;;        mov             dl,bh
;;        int             21h


use_user_address:
	clc
exit_read_ia_pci:
	ret
error_read_ia_pci:
	stc
	jmp             short exit_read_ia_pci
read_ia_pci     ENDP


;*******************************************************
;*  setup_cmd_buffs:                                   *
;*******************************************************
setup_cmd_buffs PROC    NEAR
; Setup buffer for commands
	mov      word ptr cmd_buff1, C_DONE_BIT  ; Mark buffer as available
	mov      word ptr cmd_buff2, C_DONE_BIT  ; Mark buffer as available
	mov      buff_ptrs[0], offset cgroup:cmd_buff1   ; Settup buffer 'array'
	mov      buff_ptrs[2], offset cgroup:cmd_buff2
	mov      tx_buff_no, 0                  ; Set next buffer # to use
	ret
setup_cmd_buffs ENDP

;-------------------------------------------------------------------/
;                                                                  -/
;-------------------------------------------------------------------/
setup_receive   PROC    NEAR
	.386
	mov             cx, RBD_COUNT                   ; Setup the receive buffer descriptors
	mov             ax, cs
	mov             es, ax
	mov             si, offset cgroup:start_rx_buff
	mov             bx, si                                  ; Bx holds a copy of the virtual offset

	pusha
	lea     dx,my_nexterr6
	mov     ah,09h
	int     021h
	popa

	call    cnvrt_to_phys_add

	pusha
	lea     dx,my_nexterr7
	mov     ah,09h
	int     021h
	popa

	mov             esi, eax
	mov             di, offset cgroup:start_rbd
	mov             first_rbd, di
	mov             ax, di
next_rbd:
	mov             [di].rbd_status, 0
	add             ax, size rbd_struct                             ; Pointer to the next RBD
;;if      CHIP EQ INTEL_82596
	mov             [di].rbd_link, ax
;;else
;;        movzx   eax, ax
;;        mov             [di].rbd_link, eax
;;        mov             [di].rbd_filler2, 0
;;endif
	mov             [di].rbd_ptr, esi                               ; Store physical address
	mov             [di].rbd_size, SIZE_ONE_DATA_BUFF
	mov             [di].rbd_filler1, 0
	mov             [di].rbd_voffset, bx                    ; Store the virtual offset
	add             esi, SIZE_ONE_DATA_BUFF                 ; Physical address of next buffer
	add             bx, SIZE_ONE_DATA_BUFF                  ; Virtual offset of next buffer
	mov             di, ax                                                  ; Advance to the next RBD
	loop    next_rbd

	pusha
	lea     dx,my_nexterr4
	mov     ah,09h
	int     021h
	popa

	sub             di, size rbd_struct                             ; Return to previous RBD
	mov             last_rbd, di
	or              [di].rbd_size, EL_BIT                   ; Mark end of chain
	mov             [di].rbd_link, offset cgroup:start_rbd  ; Wrap to start

	mov             cx, RFD_COUNT                           ; Setup the receive buffer descriptors
	mov             di, offset cgroup:start_rfd
	mov             first_rfd, di
	mov             ax, di
next_rfd:
	mov             [di].rfd_status, 0
	mov             [di].rfd_eol, FLEXIBLE_MODE
	add             ax, size rfd_struct                             ; Pointer to the next RFD
;;if      CHIP EQ INTEL_82596
	mov             [di].rfd_link, ax
;;else
;;        movzx   eax, ax
;;        mov             [di].rfd_link, eax
;;endif
	mov             [di].rfd_ptr, -1
	mov             [di].rfd_cnt, 0
	mov             [di].rfd_size, 0
	mov             di, ax                                                  ; Advance to the next RFD
	loop    next_rfd

	sub             di, size rfd_struct                             ; Get back to last RFD
	mov             last_rfd, di
	mov             [di].rfd_eol, EL_BIT+FLEXIBLE_MODE      ; Mark end of chain
	mov             [di].rfd_link, offset cgroup:start_rfd  ; Wrap around to start

							; Setup the start rdf to point to the start rbd
	mov             [start_rfd].rfd_ptr, offset cgroup:start_rbd    
	pusha
	lea     dx,my_nexterr4
	mov     ah,09h
	int     021h
	popa

;;if      CHIP EQ INTEL_82596
	mov             scb_buff.scb_rfa_add, offset cgroup:start_rfd
	mov             scb_buff.scb_command, RU_START
	C_ATTENTION
	
;#      mov             ax, scb_buff.scb_status         ;- Acknowledge any interrupts
;#      and             ax, SCB_STATUS_MASK
;#      mov             scb_buff.scb_command, ax
;#      C_ATTENTION                                             ; Issue a channel attention to the 82596
;;else
; debug - setup receive code required for 82557
;;endif
	pusha
	lea     dx,my_nexterr3
	mov     ah,09h
	int     021h
	popa

	ret
setup_receive   ENDP

;--------------------------------------------------------
;-    get_buffer()
;--------------------------------------------------------
get_buffer      PROC    NEAR
; Get the next buffer address from the buff_ptr array
	to_scrn 7,0,'T'

	mov             ax, cs
	mov             es, ax
	mov             ax, cs:tx_buff_no               ; Get next buffer # to use
	mov             di, ax                          ; Save into di
	mov             cs:old_buff_no, ax              ; Save buffer #
	inc             ax                                      ; Advance the buffer #
	cmp             ax, 2                           ; Advanced too far ?
	jl              short count_ok          ; No - the next count is ok
	xor             ax, ax                          ; Next count will be zero
count_ok:
	mov             cs:tx_buff_no, ax               ; Store the updated buffer #
	sal             di, 1                           ; Make bx a word count
	mov             di, cs:buff_ptrs[di]    ; Get the buffer address

	mov             ax, 1
	call    set_timeout
wait_cmd_to_complete:
	test    cs:[di], word ptr C_DONE_BIT
	jnz             short buff_finished_with
	call    do_timeout                      ; Any more time left ?
	jnz             short wait_cmd_to_complete
	mov             ax, cs:old_buff_no              ; Recover the original buffer #
	mov             cs:tx_buff_no, ax               ; Store for next time
	to_scrn 2,0,'#'

	stc
	ret

buff_finished_with:
	to_scrn 2,0,'6'

	clc
	ret
get_buffer      ENDP
;-------------------------------------------------------------
	public  wait_scb_non_active
wait_scb_non_active     PROC    NEAR
	call    set_timeout                             ; AX previously set with the timeout
;;;if      CHIP EQ INTEL_82596
check_scb_again:
	mov             ax, cs:scb_buff.scb_status ; Get the status bits
;;;else
;;;        LOAD_PORT       SCB_STATUS                      ; Load port address for the SCB status
;;;check_scb_again:
;;;        in              ax, dx                                  ; Get the status bits
;;;endif
	and             ax, CU_STATUS_BITS              ; Mask all but the cu status
	cmp             ax, CU_ACTIVE                   ; Is the cu active
	clc                                                             ; Clear carry for exit
	jne             short exit_wait_scb_non_active  ; Jump if so
	call    do_timeout                              ; No - any more time left ?
	jnz             check_scb_again                 ; Continue to wait if so
	stc                                                             ; Otherwise exit with carry set
exit_wait_scb_non_active:
	ret
wait_scb_non_active     ENDP

;--------------------------------------------------------------
	public  issue_cmd
issue_cmd       PROC    NEAR
	; di holds the start of the buffer for the command
;;if      CHIP EQ INTEL_82596
	; First check that the last command issued has been accepted
wait_cmd_accepted:
	cmp             cs:scb_buff.scb_command, 0
	jnz             wait_cmd_accepted
	; Now wait for that command to be completed
	mov             ax, 40h                                 ; Wait, if necessary, for about 1 s
;;        call    wait_scb_non_active             ; Can't issue a command if chip is active
	jc              short exit_issue_cmd    ; Exit if still active

	pushf                                                   ; Save interrupt status
	cli                                                             ; Prevent the receiver from interrupting
	cmp             cs:scb_buff.scb_command, 0      ; Did the receiver interrupt earlier
	jz              short cmd_accepted              ; No - safe to issue command
	popf                                                    ; Recover flags
	jmp             short wait_cmd_accepted ; Need to wait again

cmd_accepted:
	mov             cs:scb_buff.scb_command, CU_START
	mov             cs:scb_buff.scb_cbl_add, di
;;        STALL
	C_ATTENTION
;;        STALL
	popf                                                    ; Restore interrupt status
;;else
;;        in              ax, dx                                  ; Get the status bits
;;        and             ax, CU_STATUS_BITS              ; Mask all but the cu status
;;        cmp             ax, CU_IDLE                             ; Is the cu idle
;;        je              issue_start_cmd                 ; Need a start command if CU is idle
;;        add             dx,     SCB_CMD - SCB_STATUS; Get to the command port
;;        mov             ax, CU_RESUME
;;        out             dx, ax                                  ; Issue the command
;;        jmp             short exit_issue_cmd_ok
;;
;;issue_start_cmd:
;;        add             dx,     SCB_GEN_PTR - SCB_STATUS        ; Get to the general  pointer port
;;        xor             eax, eax
;;        mov             ax, di
;;        out             dx, eax                                 ; Issue the address - upper word is low
;;        sub             dx, SCB_GEN_PTR - SCB_CMD       ; Get back to cmd port
;       mov             al, CU_START
;       out             dx, al                                  ; Issue the command
 ;;       mov             ax, CU_START
 ;;       or              ax, 100h
;;        out             dx, ax                                  ; Issue the command
;;endif
exit_issue_cmd_ok:
	clc
exit_issue_cmd:
	ret
issue_cmd       ENDP
;--------------------------------------------------------------
;----------------------------------------------------------------
	public set_timeout
set_timeout:
;enter with ax = number of ticks (36.4 ticks per second).
	inc     ax                      ;the first times out immediately.
	mov     cs:timeout,ax
	mov     cs:timeout_counter,0
	call    latch_timer
	mov     cs:timeout_value,ax
	ret

latch_timer:
	mov     al,0                    ;latch counter zero.
	out     43h,al
	in      al,40h                  ;read counter zero.
	mov     ah,al
	in      al,40h
	xchg    ah,al
	ret

;------------------------------------------------------------------
;-------------------------------------------------------------------
	public  do_timeout
do_timeout:
;call at *least* every 27.5ms when checking for timeout.  Returns nz
;if we haven't timed out yet.
	call    latch_timer
	xchg    ax,cs:timeout_value
	sub     ax,cs:timeout_value
	shl     ax,1                    ;keep timeout in increments of 27.5 ms.
	add     cs:timeout_counter,ax   ;has the counter overflowed yet?
	jnc     do_timeout_1            ;no.
	dec     cs:timeout              ;Did we hit the timeout value yet?
	ret
do_timeout_1:
	or      sp,sp                   ;ensure nz.
	ret
;---------------------------------------------
	public  print_parameters
print_parameters:
;echo our command-line parameters
	ret

code    ends

_text   segment para public 'code'
_text   ends

init    segment para public 'code'
init    ends

	end
