ne2000.386

Warning: This file has been marked up for HTML
page ,130
;
; $name: NE2000.386
; $version: 5
; $date_modified: 12181998 
; $description: Novell NE2000 Driver Code for NetWare 386.  
; $owner:  ODI LAN Driver Manager
; Copyright (c) 1990 - 1998 Novell, Inc. All Rights Reserved.

; THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND 
; TREATIES. USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE LICENSE
; AGREEMENT ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK) THAT CONTAINS
; THIS WORK. PURSUANT TO THE SDK LICENSE AGREEMENT, NOVELL HEREBY GRANTS 
; TO DEVELOPER A ROYALTY-FREE, NON-EXCLUSIVE LICENSE TO INCLUDE NOVELL'S 
; SAMPLE CODE IN ITS PRODUCT. NOVELL GRANTS DEVELOPER WORLDWIDE 
; DISTRIBUTION RIGHTS TO MARKET, DISTRIBUTE, OR SELL NOVELL'S SAMPLE CODE
; AS A COMPONENT OF DEVELOPER'S PRODUCTS. NOVELL SHALL HAVE NO 
; OBLIGATIONS TO DEVELOPER OR DEVELOPER'S CUSTOMERS WITH RESPECT TO THIS 
; CODE.
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( History, NE2000/HISTORY )
;
;       Novell NE2000 Driver Code for NetWare 386.
;       This driver must be loaded after MSM.NLM and ETHERNET.NLM.
;
;       Written by:     DFS
;       Date:           November, 1990
;
;***********************************************************************
;
; History Log:
;
; 01-11-91 dfs  Disabled BackToBackSends to fix hang-up problems with
;               Compaq 486/33s.
;
; 06-10-91 dfs  Modified driver to new MSM spec and enabled interrupts.
;
; 09-03-91 dfs  Converted to NLM.(Version 3.20)
;
; 12-14-92 dfs  Added Shared RAM addresses FC0000, FC4000, FC8000, and
;               FCC000 and added shared RAM auto detect.(Version 3.21)
;
; 12-16-92 dfs  Deleted Shared RAM addresses FC0000 thru FCC000.
;               Added Shared RAM addresses E00000,E20000...FC0000.
;
; 01-04-92 dfs  Filled in MemoryDecode0 and Length0 if MEM= on command line
;               to prevent ParseDriverParameters from asking for frame type.
;
; 01-05-93 dfs  Added support for DriverEnableInterrupt/DriverDisableInterrupt.
;
; 01-07-93 dfs  Added shared interrupt support to DriverDisableInterrupt.
;               (Version 3.22)
;
; 01-20-93 dfs  Cleared Interrupt Status in DriverISR if in shutdown state.
;               (Version 3.23)
;
; 03-03-93 dfs  Skipped Checksum bytes when searching for NE2000 signature
;               at init.(Version 3.24)
;
; 06-17-93 dfs  - Used MLIDMemoryDecode0 instead of MLIDLinearyMemory0
;               when calculating Shared RAM control 1 and 2 value.
;               - Disabled control 1 Memory enable bit at DriverShutdown.
;               (Version 3.25)
;
; 10-06-93 akw  - Modified packet reception to copy the exact Rx packet
;               into memory (using PIO), previously 802.2 (odd) header packets
;               and odd length packets were copying in 1 extra byte before
;               and after the valid packet respectively.
;
; 11-03-93 akw  - Took out hard-coded 17 byte look ahead for 802.2 use
;               look ahead bytes copied value instead.
;
; 03-14-94 akw  - Added HSM_SPEC_VERSION string.
;               - When adjusting TotalBytes we subtract the frag size from it.
;               If the frag size is bogus (> 80000000) we overwrite memory
;               because the jump logic should be JNC instead of JNS.
;               (version 3.28)
;
; 11-08-94 jcp  Version 3.50    (Post NetWare 4.10 release)
;               - One of the variable "TransmitPage1" does not has Adapter
;                 Data Space pointer [EBP].
;               - Removed 0 dup (0).
;               - Replaced ReadTickCounter with MSMGetMicroTimer.  Function
;                 ReadTickCounter can not be ported to different platform,
;                 like UnixWare.
;
; 02-08-95 tnl  Added "slow" code to DoRAMTest write/read loops to fix
;      occasional RAM Failure errors when loading driver on Compaq
;      XL Pentium machines.
;
; 02-16-95 tnl   Added VerifyIRQ code which is called at DriverInit time to
;      verify that INT specified by user on load command line
;      matches that of the hardware. If there is a mismatch we
;      display message and error out.
;      (version 3.51)
;
; 03-02-95 tnl   Added to 2-16-95 changes additional code to verify IRQ when
;      in shared RAM mode.
;      (version 3.51)
;
; 03-03-95 tnl   Fixed problem in DriverISR receive path where bytes to copy =
;      0 case wasn't being handled properly.
;      (version 3.51)
;
; 03-16-95 tnl   Changed call to MSMPrintString to MSMPrintStringFatal
;      in DriverInit as requested by NT team.
;      (version 3.51)
;
; 03-20-95 tnl   Added 3/3/95 fix to shared RAM send path.
;      (version 3.51)
;
; 03-27-95 tnl   Changed call to MSMAlertFatal to MSMAlertWarning in DriverSend
;      code on DMANotCompleteError.
;
; 05-02-95 tnl   Took outdated APIs out of DRIVER.INC and makefile
;
; 06-07-95 tnl   Added code based on CNE2000 (MCD) code which checks to see if
;      any NE2000 cards exist before checking for SharedRAM mode.
;      (version 3.52)
;
; 06-21-95 tnl   Changed DriverInit error paths to correctly call
;      MSMReturnDriverResources in all applicable cases.
;      (version 3.53)
;
; 5 July 1995 13:47   DGM
;      Corrected some DriverInit error paths to correctly call
;      MSMReturnDriverResources in all applicable cases.
;      (version 3.54)
;
; 07-14-95 tnl   Modified VerifyIRQ to detect whether NIC is AT/LANIC or 8390.
;      If AT/LANIC (NE2000+) then we won't do any loopback testing
;      since AT/LANIC has problems with loopback mode.
;      (version 3.55)
;
; 08-22-95 tnl   Modified DriverReset to set TxFreeCount. This is to solve
;      problems where DriverReset is called internally while 
;      TxFreeCount is less than max and TxFreeCount not getting
;      set correctly; causes driver to stop transmitting.
;      (version 3.56)
;
; 25 August 1995 17:53   DGM
;   Added DriverAES and MSMScheduleAESCallBack for it in DriverInit
;   to try to overcome 8390 IMR glitch by occasionally causing
;   disableing/enabling NIC functions to be called.
;
; 13 September 1995  14:00 MPK
;       Made changes to make code compatable with 3.3 specification.  
;       Includes changes to DriverConfigTemplate (versions and Scatter
;       gap count,  Also added changes to intialization, to determine
;       BUS by use of NBI calls
;
; 01 December 1995  TNL
;       Modify CheckForSharedRAM code to fix problem where driver won't 
;   load for PIO mode card after shared RAM card was loaded first.
;
; 12 February 1996  TNL
;       Modify Config Table template to initialize MlidSlot field to -1 in
;   accordance with 3.3 spec.
;
; 13 February 1996  TNL
;       Modified DriverInit to call MSMScheduleIntTimeCallback and 
;   MSMSetHardwareInterrupt in the correct sequence as per HSM spec
;   (SPDs #115634/#117316). By doing this we can also safely remove 
;   25 August 1995 DGM code change. 
;
;   Also modified VerifyIRQ slightly to accomodate
;   NE2000 clone cards that aren't completely 100% NE2000 compatible.
;
; 28 March 1996  TNL
;       Modified DriverInit to call MSMReturnDriverResources only when
;   appropriate as shown in 3.3 spec. SPD #121140.
;
; 07 May 1996  TNL
;       Added code to DriverInit/DriverISR to take care of SMP load hang
;   problems due to DriverInit being unprotected on SMP. SPD 125034.
;
; 09 May 1996  TNL
;       Added code to CheckForSharedRAM to handle case where bogus prompt
;   for MEM is made sometimes when card is in PIO mode. SPD 124386.
;
; 04 June 1996  TNL
;       Modified VerifyIRQ code to not do explicit sti. It now calls
;   MSMYieldWithDelay which will enable ints momentarily.
;   SPD 127484.
;
; 30 October 1996 ZA
;       The following changes were made in updating the DriverConfiguration
;       Table to 3.31 specs :-
;        
;       -Set MLIDCFG_MinorVersion  to 14 instead of 13.
;       -splitted the MLIDReserved field to
;                MLIDReserved1
;                MLIDPrioritySup
;                MLIDReserved2
;
;       Also the following changes were made to the Driver Parameter Table
;       updating it to 3.31 specs:-
;
;       -The following fields were added and initialised to NULL
;         i)DriverISR2Ptr ii)DriverReserved2  iii)DriverPriorityQueuePtr
;         iv)DriverDisableInterrupt2Ptr
;       -HSMSPEC field is modified to 'HSM_SPEC_VERSION: 3.31',0
;       -A new field HSMSpecVerString is entered and initialised to HSMSPEC
;       SPD 140020
;
;  22 November 1996 ZA
;      The DriverPromiscuousChange was modified to include RemoteMultiCast
;      in order to conform to the 3.31 specs.
;
;
; 22 November 1996 ZA
;      Modified DriverReset and DriverShutdown routines to add
;      operation scope parameter.
;
; 04 July 1997 JCJ Version 3.64
;      -Changed MLIDBusTag to 0 if found bus type is ISA. SPD#160730
;      -IODetachedBit checking is removed from DriverISR since partial shutdown
;       need to be supported.  Instead now we check for MSMStatusFlags SHUTDOWN bit
;       SPD#158389
;  AYD -Verify IRQ only if it is Shared RAM card SPD#132446
;
; 18 December 1997 WTT Version 3.65
;   Fixed code to initialized all fields to a known state in 
;   the AdapterOptions structure AdapterOptionDefinitionStructure.
;   The specification states that all unused fields must be set to 
;   zero. The code was assuming that the assembler would set all 
;   fields that did not have a specified value to zero. Newer 
;   versions of the assembler are not doing this.
;   SPD 174346
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
        name    NE2000
        title   NE2000 LAN Driver (HSM Version)
        subttl  -- Structures and Equate Values --
        page


include driver.inc
        subttl  -- NE2000 Specific Equates --
        page
;
BackToBackSends         equ     -1
UseFastCalls            equ     -1


;
;***********************************************************************\
;                                                                       *
;       Equates.                                                        *
;                                                                       *
;***********************************************************************/
;
; Command Register Bits.
;
NIC_Page0               equ     22h
NIC_Page1               equ     62h
NIC_Page0DMAWrite       equ     12h
NIC_Page2DMAWrite       equ     92h
NIC_RemoteDmaWr         equ     12h
NIC_RemoteDmaRd         equ     0ah
NIC_AbortDmaWr          equ     32h
NIC_AbortDmaRd          equ     2ah
NIC_Page0Stop           equ     21h
NIC_Page1Stop           equ     61h
NIC_Transmit            equ     26h
NIC_TxInProgress        equ     04h
;
; Interrupt Mask Register Bits.
;
NIC_MaskByte            equ     00h
NIC_UnmaskByte          equ     1fh
;
; Interrupt Status Register Bits.
;
NIC_PacketReady         equ     15h
NIC_RecvError           equ     14h
NIC_TxComplete          equ     0ah
NIC_TransmitError       equ     08h
;
; Transmit Status Register Bits.
;
NIC_Collisions          equ     04h
NIC_ExcessCollisions    equ     08h
NIC_CarrierSenseLost    equ     10h
NIC_FIFO_Underrun       equ     20h
NIC_CDHeartBeat         equ     40h
NIC_OutOfWindowColl     equ     80h
;
; Receive Status Register Bits.
;
CRCErrorBit             equ     02h
FrameCRCErrorBits       equ     06h
FIFOOverrunErrorBit     equ     08h
MissedPacketErrorBit    equ     10h
;
; Receive Configuration Bits.
;
NIC_Save_Errors         equ     01h
NIC_Save_Runts          equ     02h
NIC_Broadcast           equ     04h
NIC_Multicast           equ     08h
NIC_Promiscuous         equ     10h
NIC_Monitor             equ     20h
;
PSTART                  equ     4Ch     ;12 pages for send (enough for 2 buffers)
PSTOP                   equ     80h
;
RAMPSTART               equ     0Ch     ;12 pages for send (enough for 2 buffers)
RAMPSTOP                equ     40h
;
; More NIC Register Equates...
;
PhysicalReg0            equ     PageStart
TransmitPage            equ     TransmitStatus
NumberCollisions        equ     TransmitByteCount0
FIFO                    equ     TransmitByteCount1
Current                 equ     InterruptStatus
ReceiveStatus           equ     ReceiveConfiguration
CurrentDMA0             equ     RemoteStartAddress0
CurrentDMA1             equ     RemoteStartAddress1
MultiCastAddrReg        equ     RemoteStartAddress0
TallyCounter0           equ     TransmitConfiguration
TallyCounter1           equ     DataConfiguration
TallyCounter2           equ     InterruptMask
SizeOfCRCField          equ     4
;
; Maximum Send Retries.
;
MaxRetries              equ     10

;
; Shared RAM mode INT mask for Mode Configuration Register INT bits 3-5.
;

IntMask         equ   38h

PUBLIC        DriverCallBack
PUBLIC        DriverISR
PUBLIC        DriverMulticastChange
PUBLIC        DriverReset
PUBLIC        DriverSend
PUBLIC        DriverShutdown
PUBLIC        DriverPromiscuousChange
PUBLIC        DriverEnableInterrupt
PUBLIC        DriverDisableInterrupt

        subttl  -- NE2000 Specific Structures --
        page
;
;***********************************************************************\
;                                                                       *
; NE2000 Structures.                                                    *
;                                                                       *
;***********************************************************************/
;
;
;***************************************************************\
;                                                               *
; MulticastTableStructure.                                      *
;                                                               *
;***************************************************************/
;
MulticastTableStructure struc

        MulticastAddress        db      6 dup (0)
        EntryUsed               dw      0

MulticastTableStructure ends
;
;***************************************************************\
;                                                               *
; NE2000 I/O register structure.                                *
;                                                               *
;***************************************************************/
;
NIC     struc
        ICommand                        db      ?               ; 00h
        IPageStart                      db      ?               ; 01h
        IPageStop                       db      ?               ; 02h
        IBoundary                       db      ?               ; 03h
        ITransmitStatus                 db      ?               ; 04h
        ITransmitByteCount0             db      ?               ; 05h
        ITransmitByteCount1             db      ?               ; 06h
        IInterruptStatus                db      ?               ; 07h
        IRemoteStartAddress0            db      ?               ; 08h
        IRemoteStartAddress1            db      ?               ; 09h
        IRemoteByteCount0               db      ?               ; 0Ah
        IRemoteByteCount1               db      ?               ; 0Bh
        IReceiveConfiguration           db      ?               ; 0Ch
        ITransmitConfiguration          db      ?               ; 0Dh
        IDataConfiguration              db      ?               ; 0Eh
        IInterruptMask                  db      ?               ; 0Fh
        INICData                        db      ?               ; 10h
                                        db      14 dup (?)      ; 11h
        IReset                          db      ?               ; 1Fh
NIC     ends

NICSharedRAM    struc
        IControl1                       db      ?               ; 00h
        IATDetect                       db      ?               ; 01h
                                        db      3 dup (?)       ; 02h
        IControl2                       db      ?               ; 05h
                                        db      2 dup (?)       ; 06h
        IProm                           db      8 dup (?)       ; 08h
NICSharedRAM    ends

IOConfigurationStructure        struc
        CLink                   dd      ?
        CFlags                  dw      ?
        CSlot                   dw      ?
        CIOPortsAndLengths      dw      4 dup (?)
        CMemoryDecode0          dd      ?
        CMemoryLength0          dw      ?       ; In Paragraphs
        CMemoryDecode1          dd      ?
        CMemoryLength1          dw      ?
        CInterrupt              db      2 dup (?)
        CDMAUsage               db      2 dup (?)
        CResourceTag            dd      ?
        CDriverConfig           dd      ?
        CCommandString          dd      ?
        CLogicalName            db      18 dup (?)
        CLinearMemory           dd      2 dup (?)
        CChannelNumber          dw      ?
        CIOReserved             db      6 dup (?)
IOConfigurationStructure        ends

ITransmitPage   equ     ITransmitStatus
;
;***************************************************************\
;                                                               *
; Ethernet Receive Header Structure.                            *
;                                                               *
;***************************************************************/
;
ReceiveHeaderStructure  struc

        RReceiveStatus          db      ?
        RNextBuffer             db      ?
        RByteCount              dw      ?

ReceiveHeaderStructure  ends
;
;***************************************************************\
;                                                               *
; Start of the Adapter Data Space structure for NE2000.         *
;                                                               *
;***************************************************************/
;
                                                                ;JCP
GenericVariableBegin    equ     offset TotalTxPacketCount       ;941108 *Begin*
GenericVariableEnd      equ     offset CustomVariableCount
CustomVariableBegin     equ     offset UnderrunErrorCount
CustomVariableEnd       equ     offset VectorToTheStrings       ;941108 *End*

DriverAdapterDataSpace  struc

TxStartTime             dd      0       ; Used to detect timeouts.

TransmitSize1           dd      0       ; Used to store Page 1 Tx size.
TransmitSize2           dd      0       ; Used to store Page 2 Tx size.
;
;***************************************************************\
;                                                               *
; TransmitStatusFlag tell us the status of the Send buffers.    *
;                                                               *
;       00x = Sending packet -- waiting for TxComplete.         *
;       0x0 = Packet we are sending is in Page2.                *
;       x00 = Data transferred to board, waiting for send       *
;             to complete.                                      *
;                                                               *
; Example:                                                      *
; Start         010     Nothing being sent, page 1 is free.     *
; SendPacket    001     Sending Page 1.                         *
; SendPacket    101     Data transferred to Page 2, still       *
;                       waiting for page 1.                     *
; TxComplete    011     Sending Page2, page 1 is free.          *
; SendPacket    111     Data in Page1, still waiting for        *
;                       Page 2 to complete send.                *
; etc.                                                          *
;                                                               *
;***************************************************************/
;
TransmitStatusFlag      db      00000010b       ; Not sending, Page 1 free.

;;InDriverISR           db      0               ;; IF NOT UseFastCalls

NextPage                db      PSTART + 1      ; Used to detect page errors.
;
RxFragments             dd      0
OddValue                db      0
OddByteValue            db      0
RetryTxFlag             db      0       ; Used if Receive overflow occurs
OverflowRestartFlag     db      0       ; Used if Receive overflow occurs.

FirstTimeInit           db      1       ; Start out in driver init.

RetryCounter            dd      MaxRetries      ; Tx Retry counter.

NICRAMSegmentBase       dd      0       ; Shared RAM base.
NICRAMSegmentLimit      dd      0       ; Recieve ring limit.
NICRAMReceiveRingStart  dd      0       ; Receive ring start.

TransmitPage0           db      0c0h    ; 1st I/O Transmit Page.
TransmitPage1           db      0c6h    ; 2nd I/O Transmit Page.

CRCWorkArea             db      6 dup (0)       ;Used for multicast hashing.

ReceiveStatusTest       db      4eh             ; Test Receive Status.
TotalBytes              dd      0               ; Receive Bytes var.

Control1Value   db      0                       ; For shared RAM.
Control2Value   db      0
CurrentPacketPointer    dd      0       ; Holds packet address during Rx.
CurrentECB              dd      0       ; Holds ECB during Frag Receive.

; 0 dup (0) generate a warning message. Therefore we need a value here to
; prevent AlignIOFour get into this condition. JCP, 941108.

AllowAlignIOFour        db      1 dup (0)       ; Can be 1 or 3 dup (0). JCP.
AlignIOFour             db      (4 - offset (AlignIOFour and 3)) and 3 dup (?)  ; Align 4
;
;***************************************************************\
;                                                               *
; NE2000 I/O Register Values.                                   *
;                                                               *
;***************************************************************/
;
        Control1                        dd      ?       ; Shared RAM
        Control2                        dd      ?       ; Shared RAM
        Prom                            dd      ?       ; Shared RAM

        Command                         dd      ?       ; I/O Base + 00h
        PageStart                       dd      ?       ; I/O Base + 01h
        PageStop                        dd      ?       ; I/O Base + 02h
        Boundary                        dd      ?       ; I/O Base + 03h
        TransmitStatus                  dd      ?       ; I/O Base + 04h
        TransmitByteCount0              dd      ?       ; I/O Base + 05h
        TransmitByteCount1              dd      ?       ; I/O Base + 06h
        InterruptStatus                 dd      ?       ; I/O Base + 07h
        RemoteStartAddress0             dd      ?       ; I/O Base + 08h
        RemoteStartAddress1             dd      ?       ; I/O Base + 09h
        RemoteByteCount0                dd      ?       ; I/O Base + 0ah
        RemoteByteCount1                dd      ?       ; I/O Base + 0bh
        ReceiveConfiguration            dd      ?       ; I/O Base + 0ch
        TransmitConfiguration           dd      ?       ; I/O Base + 0dh
        DataConfiguration               dd      ?       ; I/O Base + 0eh
        InterruptMask                   dd      ?       ; I/O Base + 0fh
        NICData                         dd      ?       ; I/O Base + 10h
        Reset                           dd      ?       ; I/O Base + 1fh
;
;***************************************************************\
;                                                               *
; Look ahead buffer.                                            *
;                                                               *
;***************************************************************/
;
ReceiveHeader   db      128+22+4 dup (0)
;
;***************************************************************\
;                                                               *
;       Error Counters.                                         *
;                                                               *
;***************************************************************/
;
StatisticsVersion       db      03, 00
GenericVariableCount    dw      (GenericVariableEnd - GenericVariableBegin) / 4
NotSupportedMask0       dd      11111011110010000000000000000011b

;GenericVariableBegin           db      0 dup (?)       ; JCP, 941108.
        TotalTxPacketCount      dd      0               ; 1 - (Used by MSM)
        TotalRxPacketCount      dd      0               ; 1 - (Used by MSM)
        NoECBAvailableCount     dd      0               ; 1 - (Used by MSM)
        PacketTxTooBigCount     dd      0               ; 1 - not used
        PacketTxTooSmallCount   dd      0               ; 1 - not used
        PacketRxOverflowCount   dd      0               ; 0 - Used by driver
        PacketRxTooBigCount     dd      0               ; 1 - not used
        PacketRxTooSmallCount   dd      0               ; 1 - not used
        PacketTxMiscErrorCount  dd      0               ; 1 - not used
        PacketRxMiscErrorCount  dd      0               ; 1 - not used
        RetryTxCount            dd      0               ; 0 - Used by driver
        ChecksumErrorCount      dd      0               ; 0 - Used by driver
        HardwareRxMismatchCount dd      0               ; 1 - (Used by MSM)
        TotalTxOKByteCountLow   dd      0               ; 0 - Used by MSM
        TotalTxOKByteCountHigh  dd      0               ; 0 - Used by MSM
        TotalRxOKByteCountLow   dd      0               ; 0 - Used by MSM
        TotalRxOKByteCountHigh  dd      0               ; 0 - Used by MSM
        TotalGroupAddrTxCount   dd      0               ; 0 - Used by MSM
        TotalGroupAddrRxCount   dd      0               ; 0 - Used by MSM
        AdapterResetCount       dd      0               ; 0 - Used by driver
        AdapterOprTimeStamp     dd      0               ; 0 - Used by MSM
        QDepth                  dd      0               ; 0 - Used by MSM

        TxOKSingleCollision     dd      0               ; 0 - Used by driver
        TxOKMultipleCollisions  dd      0               ; 0 - Used by driver
        TxOKButDeferred         dd      0               ; 0 - not used
        TxAbortLateCollision    dd      0               ; 0 - Used by driver
        TxAbortExcessCollisions dd      0               ; 0 - Used by driver
        TxAbortCarrierSense     dd      0               ; 0 - Used by driver
        TxAbortExDeferral       dd      0               ; 0 - not used
        RxAbortFrameAlignment   dd      0               ; 0 - Used by driver
;GenericVariableEnd             db      0 dup (?)       ; JCP, 941108

CustomVariableCount             dw      (CustomVariableEnd - CustomVariableBegin) / 4

;CustomVariableBegin            db      0 dup (?) ; JCP, 941108.
        UnderrunErrorCount      dd      0       ; Tx FIFO Underrun
        TransmitTimeoutCount    dd      0       ; Tx Timeouts

        RxPagingErrorCount      dd      0       ; Rx Paging Errors
        RxFIFOOverrunErrorCount dd      0       ; Rx FIFO Overrun
        RxMissedPacketCount     dd      0       ; Rx Missed Packet
        GotNothingCount         dd      0       ; Rx Got Nothing

        UnsupportedFrameCount   dd      0       ; Frame not registered counter.
        UnsupportedMulticastCount dd    0       ; Bad multicast received.
        BackToBackSendCount     dd      0       ; Back to Back send counter.
        EnqueueSendCount        dd      0       ; Tx Queue counter.
;CustomVariableEnd              db      0 dup (?) ;JCP, 941108

VectorToTheStrings      dd      offset DiagnosticsStrings

AllowAlignDEndVA        db      1 dup (0)       ;JCP, 941108. Refer AlignIOFour.
AlignDEndVA             db      (4 - offset (AlignDEndVA and 3)) and 3 dup (?)  ; Align 4 for MOVSD

DriverAdapterDataSpace  ends
        subttl  -- OSDATA Data Segment --
        page
        assume  cs: OSCODE, ds: OSDATA, es: OSDATA, ss: OSDATA
;
;***********************************************************************\
;                                                                       *
; The following variables are common to the entire driver.              *
;                                                                       *
;***********************************************************************/
;
OSDATA  segment rw public 'DATA'

HSMSPEC                 db      'HSM_SPEC_VERSION: 3.31',0
;
;***************************************************************\
;                                                               *
; Statistic Diagnostic Strings.                                 *
;                                                               *
;***************************************************************/
;
DiagnosticsStrings      dw      (EndOfStrings-DiagnosticsStrings)

        db      'UnderrunErrorCount',0
        db      'TransmitTimeoutCount',0
        db      'RxPagingErrorCount', 0
        db      'ReceiveFIFOOverrunErrorCount', 0
        db      'ReceiverMissedPacketCount', 0
        db      'GotNothingCount',0
        db      'UnsupportedFramePacketCount',0
        db      'UnsupportedMulticastCount', 0
        db      'BackToBackSendCount', 0
        db      'EnqueuedSendsCount', 0

        db      0,0

EndOfStrings    equ     $
;
;***************************************************************\
;                                                               *
; Driver Parameter Block to pass to MSM.                        *
;                                                               *
;***************************************************************/
;
        align   4
DriverParameterBlock            label   dword
DriverParameterSize             dd      DriverParameterBlockSize
DriverStackPointer              dd      0
DriverModuleHandle              dd      0
DriverBoardPointer              dd      0
DriverAdapterPointer            dd      0
DriverConfigTemplatePtr         dd      DriverConfigTemplate
DriverFirmwareSize              dd      0
DriverFirmwareBuffer            dd      0
DriverNumKeywords               dd      1
DriverKeywordText               dd      NE2000KeywordText
DriverKeywordTextLen            dd      NE2000TextLen
DriverProcessKeywordTab         dd      NE2000ProcessKeywordTab
DriverAdapterDataSpaceSize      dd      SIZE DriverAdapterDataSpace
DriverAdapterTemplate           dd      DriverAdapterDataSpaceTemplate
DriverStatisticsTable           dd      StatisticsVersion
DriverEndOfChainFlag            dd      0
DriverSendWantsECBs             dd      0
DriverMaxMulticast              dd      -1
DriverNeedsBelow16Meg           dd      0

DriverAESPtr                    dd      0
DriverCallBackPtr               dd      offset DriverCallBack
DriverISRPtr                    dd      offset DriverISR
DriverMulticastChangePtr        dd      offset DriverMulticastChange
DriverPollPtr                   dd      0
DriverResetPtr                  dd      offset DriverReset
DriverSendPtr                   dd      offset DriverSend
DriverShutdownPtr               dd      offset DriverShutdown
DriverTxTimeoutPtr              dd      0
DriverPromiscuousChangePtr      dd      offset DriverPromiscuousChange
DriverStatisticsChangePtr       dd      0
DriverRxLookAheadChangePtr      dd      0
DriverManagementPtr             dd      0
DriverEnableInterruptPtr        dd      offset DriverEnableInterrupt
DriverDisableInterruptPtr       dd      offset DriverDisableInterrupt
DriverISR2Ptr                   dd      0
DriverReserved2                 dd      0
HSMSpecVerString                dd      HSMSPEC
DriverPriorityQueuePtr          dd      0
DriverDisableInterrupt2Ptr      dd      0

DriverParameterBlockSize        equ     $ - DriverParameterBlock
;
;***************************************************************\
;                                                               *
; Copy of Virtual Adapter Data area to be copied at             *
; initialization.                                               *
;                                                               *
;***************************************************************/

DriverAdapterDataSpaceTemplate  DriverAdapterDataSpace  <>

;DriverConfigTemplate    db      0 dup (?)      ; JCP, 941108
DriverConfigTemplate     label   byte           ; JCP, 941108
        db      'HardwareDriverMLID        '    ; [ebx].MLIDCFG_Signature
        db      01                              ; [ebx].MLIDCFG_MajorVersion
        db      14                              ; [ebx].MLIDCFG_MinorVersion
        db      6 dup (0ffh)                    ; [ebx].MLIDNodeAddress
        dw      0010010001001001b               ; [ebx].MLIDModeFlags
        dw      0000                            ; [ebx].MLIDBoardNumber
        dw      0000                            ; [ebx].MLIDBoardInstance
        dd      00000000                        ; [ebx].MLIDMaximumSize
        dd      00000000                        ; [ebx].MLIDMaxRecvSize
        dd      00000000                        ; [ebx].MLIDRecvSize
        dd      00000000                        ; [ebx].MLIDCardName
        dd      DriverNICShortName              ; [ebx].MLIDshortName
        dd      00000000                        ; [ebx].MLIDFrameType
        dw      0000                            ; [ebx].MLIDReserved0
        dw      0000                            ; [ebx].MLIDFrameID
        dw      0001                            ; [ebx].MLIDTransportTime
        dd      000000000                       ; [ebx].MLIDRouteHandler
        dw      10                              ; [ebx].MLIDLineSpeed
        dw      0000                            ; [ebx].MLIDLookAheadSize
        db      02                              ; [ebx].MLIDCFG_SGCount
        db      00                              ; [ebx].MLIDReserved1
        dw      0000                            ; [ebx].MLIDPrioritySup
        dd      00000000                        ; [ebx].MLIDReserved2
        db      00                              ; [ebx].MLIDMajorVersion
        db      00                              ; [ebx].MLIDMinorVersion
        dw      0000000000000000b               ; [ebx].MLIDFlags
        dw      0010                            ; [ebx].MLIDSendRetries
        dd      00000000                        ; [ebx].MLIDLink
        dw      0000                            ; [ebx].MLIDSharingFlags
        dw      0FFFFh                          ; [ebx].MLIDSlot
        dw      0300h, 32, 0, 0                 ; [ebx].MLIDIOPortsAndLengths
        dd      00000000                        ; [ebx].MLIDMemoryDecode0
        dw      0000                            ; [ebx].MLIDLength0
        dd      00000000                        ; [ebx].MLIDMemoryDecode1
        dw      0000                            ; [ebx].MLIDLength1
        db      03, 0FFh                        ; [ebx].MLIDInterrupt
        db      0FFh, 0FFh                      ; [ebx].MLIDDMAUsage
        dd      00000000                        ; [ebx].MLIDResourceTag
        dd      00000000                        ; [ebx].MLIDConfiguration
        dd      00000000                        ; [ebx].MLIDCommandString
        db      18 dup (0)                      ; [ebx].MLIDLogicalName
        dd      00000000                        ; [ebx].MLIDLinearMemory0
        dd      00000000                        ; [ebx].MLIDLinearMemory1
        dw      0000                            ; [ebx].MLIDChannelNumber
        dd      00000000                        ; [ebx].MLIDBusTag
        db      00                              ; [ebx].MLIDIOCfgMajorVersion
        db      00                              ; [ebx].MLIDIOCfgMinorVersion

Message         DriverNICShortName,     'NE2000'
;
;***************************************************************\
;                                                               *
; Parameters required by ParseDriverParameters.                 *
;                                                               *
;***************************************************************/
;
IOPort0Data             dd      7, 300h, 320h, 340h, 360h, 240h, 280h, 2C0h
Interrupt0Data          dd      8, 2, 3, 4, 5, 10, 11, 12, 15
MemoryDecode0Data       dd      24, 0d0000h, 0d4000h, 0d8000h, 0dc000h
                        dd      0c0000h, 0c4000h, 0c8000h, 0cc000h
                        dd      0e00000h, 0e20000h, 0e40000h, 0e60000h
                        dd      0e80000h, 0ea0000h, 0ec0000h, 0ee0000h
                        dd      0f00000h, 0f20000h, 0f40000h, 0f60000h
                        dd      0f80000h, 0fa0000h, 0fc0000h, 0fe0000h

AdapterOptions AdapterOptionDefinitionStructure <0,IOPort0Data,0,0,0,MemoryDecode0Data,0,0,0,Interrupt0Data,0,0,0,0>
;
;***************************************************************\
;                                                               *
; Custom Keyword information.                                   *
;                                                               *
;***************************************************************/
;
MemText                 db      'MEM'           ; Break into debugger keyword.
MemTextLen              equ     $-MemText OR T_HEX_NUMBER
                        dd      000C0000h       ; Minimum.
                        dd      00FE0000h       ; Maximum.
                        dd      MemDefault      ; Default String.
                        dd      MemValid        ; Valid Characters string.
                        dd      MemPrompt       ; Prompt String.

NE2000KeywordText       dd      MemText         ; First Keyword.
NE2000TextLen           dd      MemTextLen      ; First Keywords length.

NE2000ProcessKeywordTab dd      MemRoutine      ; First Keyword routine.

MemDefault              db      "D0000", 0
MemValid                db      "02468ACDEF", 0
MemPrompt               db      "Supported memory address values are "
MemPromptValues         db      256 dup (0)
MemPromptStrings        db      "D0000"
                        db      "D4000"
                        db      "D8000"
                        db      "DC000"
                        db      "C0000"
                        db      "C4000"
                        db      "C8000"
                        db      "CC000"
                        db      "E00000"
                        db      "E20000"
                        db      "E40000"
                        db      "E60000"
                        db      "E80000"
                        db      "EA0000"
                        db      "EC0000"
                        db      "EE0000"
                        db      "F00000"
                        db      "F20000"
                        db      "F40000"
                        db      "F60000"
                        db      "F80000"
                        db      "FA0000"
                        db      "FC0000"
                        db      "FE0000"
MemPromptTail           db      CR, LF, "Memory address : ", 0
MemPromptTailSize       equ     $ - MemPromptTail

MemOnCommandLine        db      0
InitMem                 dd      0
AdapterIsSharedRAM      db      0

InDriverInit      db   0   ;SPD 125034

;
;***************************************************************\
;                        *
; Interrupt lookup table for Shared RAM mode. Indexed by the   *
; value of bits 3-5 of the ATLANTIC Mode Config register A.   *
;                                                               *
;***************************************************************/
;

SharedRAMIntTable   db   3, 4, 5, 2, 10, 11, 12 ,15

;
;
;***************************************************************\
;                                                               *
; Message equates.                                              *
;                                                               *
;***************************************************************/
;
CR                      equ     13
LF                      equ     10
;
;***************************************************************\
;                                                               *
; Run-time error message strings.                               *
; NE2000 error messages.                                        *
;                                                               *
;***************************************************************/
;
TransmitTimeoutMessage  db      66, 00, 'The cable might be disconnected on the board.', CR, LF, 0
DMANotCompleteError     db      200, 00, "The board's DMA did not complete the write.", CR, LF, 0
;
;***************************************************************\
;                                                               *
; InitNIC error message strings.                                *
;                                                               *
;***************************************************************/
;
NICNotInSlotMessage     db      50, 00, 'The board cannot be found.', CR, LF, 0
PortFailMessage         db      54, 00, 'The board did not respond to the initialization command.', CR, LF, 0
BufferMemoryFailMessage db      51, 00, 'Board RAM failed the memory test.', CR, LF, 0
NICIn8BitSlotMessage    db      223, 00, 'The board must be placed in a 16-bit slot.', CR, LF, 0
NICIsNE1000Message      db      224, 00, 'This board is configured as an NE1000.', CR, LF, 0
NICBadConfiguration     db      80, 00, 'The board could not be configured.', CR, LF, 0
IRQInvalidMessage   db      91, 00, 'The hardware configuration conflicts.', CR, LF, 0
;
OSDATA  ends
        subttl  -- DriverChangeMulticast --
        page
OSCODE  segment er public 'CODE'

DEBUG equ 0

if DEBUG
        extrn   OutChar: near
endif

;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( DriverMulticastChange, NE2000/API/MULTI )
;
; Name:         DriverMulticastChange
;
; Description:  This routine will modify the NIC's multicast registers to
;               enable it to receive the multicast addresses listed in
;               the multicast table. Each entry in the multicast table is as
;               follows:
;
;               bytes 0-5 = Multicast Address.
;               bytes 6-7 = Entry used(Non zero if used).
;
; On Entry:     EAX     N/A
;               EBX     N/A
;               ECX     # of Entries in Table( 0 if empty )
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     @ Multicast Table
;               EDI     N/A
;
;               Note:   Interrupts are in any state.
;
; On Return:    EAX     Destroyed
;               EBX     Preserved
;               ECX     Destroyed
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Destroyed
;               EDI     Destroyed
;
;               Flags:
;
;               Note:   Interrupts preserved.
;
; Remarks:      This routine is called by the ethernet media module.
;               It can be called at process or interrupt time.
;
; See Also:     ETHERTSM\EtherTSMAddMulticastAddress
;               ETHERTSM\EtherTSMDeleteMulticastAddress
;               ETHERTSM\EtherTSMUpdateMulticast
;               CalculateHash
;               ModifyNICHashTableBit
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
DriverMulticastChange   proc
;
;***************************************************************\
;                                                               *
; First reset Multicast Address Registers.                      *
;                                                               *
;***************************************************************/
;
        mov     edx, [ebp].Command              ; Set for page one (62h)
        mov     al, NIC_Page1
        out     dx, al

        xor     al, al                          ; Set all bits to zero.
        mov     edx, [ebp].MultiCastAddrReg     ; Point at NIC address.

        mov     edi, 8
SetAllBits:
        out     dx,al                           ; Send it out.
        inc     dx                              ; Go to next register
        dec     edi
        jnz     short SetAllBits                ; and send it out.

        mov     edx, [ebp].Command              ; Set back to page 0 (22h)
        mov     al,NIC_Page0
        out     dx, al

        or      ecx, ecx
        jz      short MulticastChangeExit       ; Leave if table is empty.
;
;***************************************************************\
;                                                               *
; Now Loop thru each table entry and set multicast addresses    *
; that use used.                                                *
;                                                               *
;***************************************************************/
;
ChangeMulticastLoop:
;
;***************************************************************\
;                                                               *
; Get value to put into controllers multicast hash table.       *
;                                                               *
;***************************************************************/
;
        push    ecx                             ; Save loop counter.
        call    CalculateHash
;
;***************************************************************\
;                                                               *
; Tell NIC to allow this address.                               *
;                                                               *
;***************************************************************/
;
        call    ModifyNICHashTableBit
        pop     ecx                             ; Restore loop counter.

NextMulticastEntry:
        add     esi, SIZE MulticastTableStructure       ; Get next entry.
        dec     ecx
        jnz     short ChangeMulticastLoop
MulticastChangeExit:
        mov     edx, [ebp].ReceiveConfiguration
        mov     al, NIC_Multicast OR NIC_Broadcast
        out     dx, al
        ret

DriverMulticastChange   endp
        subttl  -- CalculateHash --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( CalculateHash, NE2000/INTERNAL/HASH )
;
; Name:         CalculateHash
;
; Description:  This routine will take the 6-byte multicast address
;               pointed to by ESI and calculate a CRC as the 8390 would. It
;               will return in EAX the upper 6-bits of the CRC. This is used
;               as an index into the 8390 multicast hash address table.
;
; On Entry:     EAX     N/A
;               EBX     N/A
;               ECX     N/A
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     @ Multicast Address
;               EDI     N/A
;
;               Note:   Interrupts are in any state.
;
; On Return:    EAX     Upper 6 bits of CRC
;               EBX     Preserved
;               ECX     Preserved
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Preserved
;               EDI     Destroyed
;
;               Flags:
;
;               Note:   Interrupts preserved.
;
; Remarks:      This routine is called by DriverMulticastChange.
;               It can be called at process or interrupt time.
;
; See Also:     DriverMulticastChange.
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
CalculateHash   proc

        push    esi                             ; Save -> to buffer.
        push    ebx
        mov     eax, [esi]                      ; Copy of address into a
        mov     dword ptr [ebp].CRCWorkArea, eax
        mov     ax, [esi + 4]                   ; work area.
        mov     word ptr [ebp].CRCWorkArea + 4, ax
;
;***************************************************************\
;                                                               *
; EBX = CRC = 0xFFFFFFFFh                                       *
;                                                               *
;***************************************************************/
;
        mov     ebx, 0ffffffffh
;
;***************************************************************\
;                                                               *
; for (index=0; index< HASHNUMBER(6); ++ index)                 *
;                                                               *
;***************************************************************/
;
        mov     ecx, 6                          ; Outer loop = bytes in addr.
        lea     esi, [ebp].CRCWorkArea          ; Initate index to nic address.
FilterOuterLoop:
        push    ecx                             ; Save outer loop counter.
;
;***************************************************************\
;                                                               *
; for (j=0; j<8; ++j)                                           *
;                                                               *
;***************************************************************/
;
        mov     ecx, 8                          ; Set innerloop count.

FilterInnerLoop:
;
;***************************************************************\
;                                                               *
; carry = ( (crc&0x80000000) ? 1 : 0) ^ (multaddress[i] & 01)   *
;                                                               *
;***************************************************************/
;
        mov     edx, 80000000h
        and     edx, ebx                        ; EDX = CRC AND 80000000h.

        rcl     edx, 2                          ; Convert result to a 1 or 0.

        mov     ax, 1
        and     ax, [esi]                       ; Multaddress[i] & 01.
        cwde                                    ; Convert to doubleword.

        xor     eax, edx
;
;***************************************************************\
;                                                               *
; crc <<= 1                                                     *
;                                                               *
;***************************************************************/
;
        rcl     ebx, 1
;
;***************************************************************\
;                                                               *
; Multaddress[i] >>= 1                                          *
;                                                               *
;***************************************************************/
;
        shr  byte ptr [esi], 1
;
;***************************************************************\
;                                                               *
;if(carry)                                                      *
;       crc = ((crc^POLYNOMIAL) | carry)                        *
;                                                               *
;***************************************************************/
;
        cmp     eax, 0                          ; AX has carry in it.
        jz      short FinshInnerFilterPass      ; Jump if no carry.
;
;***************************************************************\
;                                                               *
; crc = crc ^ Polynomial                                        *
;                                                               *
;***************************************************************/
;
        xor     ebx, 04c11db6h
;
;***************************************************************\
;                                                               *
; crc = crc | carry                                             *
;                                                               *
;***************************************************************/
;
        or      ebx, eax

FinshInnerFilterPass:
        dec     ecx
        jnz     FilterInnerLoop

        inc     esi                             ; Point to next multicast byte.
        pop     ecx                             ; Get outer loop counter.
        dec     ecx
        jnz     FilterOuterLoop
;
;***************************************************************\
;                                                               *
; crc = crc & 0x0ff000000h                                      *
;                                                               *
;***************************************************************/
;
        and     ebx, 0ff000000h
;
;***************************************************************\
;                                                               *
; crc = crc >> 26                                               *
;                                                               *
;***************************************************************/
;
        rcr     ebx, 26
;
;***************************************************************\
;                                                               *
; AX has the filterbit.                                         *
;                                                               *
;***************************************************************/
;
        movzx   eax, bx                         ; Make a 32bit number.
        pop     ebx
        pop     esi                             ; Restore pointer.
        ret

CalculateHash   endp
        subttl  -- ModifyNICHashTableBit --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( ModifyNICHashTableBit, NE2000/INTERNAL/HASHBIT )
;
; Name:         ModifyNICHashTableBit
;
; Description:  This routine will modify a bit in the 8390 multicast hash
;               table.
;
; On Entry:     EAX     6 bit index value
;               EBX     N/A
;               ECX     N/A
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     N/A
;               EDI     N/A
;
;               Note:   Interrupts are in any state.
;
; On Return:    EAX     Destroyed
;               EBX     Preserved
;               ECX     Preserved
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Preserved
;               EDI     Destroyed
;
;               Flags:
;
;               Note:   Interrupts preserved.
;
; Remarks:      This routine is called by DriverMulticastChange.
;               It can be called at process or interrupt time.
;
; See Also:     DriverMulticastChange
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
ModifyNICHashTableBit   proc

        mov     edi, eax                        ; Get copy of index.

        mov     al, 62h                         ; Tell nic that we want to
        mov     edx, [ebp].Command              ; select the page1 registers.
        out     dx, al                          ; Issue the command.

        mov     eax, edi                        ; Get the index back.
        shr     edi, 3                          ; Divide it by 8.  This
                                                ; gives us which NIC Multicast
                                                ; address register to use.
        mov     edx, [ebp].MultiCastAddrReg     ; Point at appro. nic address.
        add     edx, edi
        shl     edi, 3                          ; Get back register index .
        xor     edi, eax                        ; Get bit index into register.

        in      al, dx                          ; Get current reg bit settings.
        bts     eax, edi                        ; Set approriate bit.
        out     dx, al                          ; Write new value.

        mov     edx, [ebp].Command              ; Set nic back to page0 regs.
        mov     al, 22h
        out     dx, al

        ret                                     ;Done

ModifyNICHashTableBit   endp
        subttl  -- DriverPromiscuousChange --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( DriverPromiscuousChange, NE2000/API/PROMISCU )
;
; Name:         DriverPromiscuousChange
;
; Description:  This routine will enable/disable the Promiscuous Mode.
;
; On Entry:     EAX     N/A
;               EBX     N/A
;               ECX     0 to disable the Promiscuous mode
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     @ Multicast Table
;               EDI     N/A
;
;               Note:   Interrupts are in any state.
;
; On Return:    EAX     Destroyed
;               EBX     Preserved
;               ECX     Destroyed
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Destroyed
;               EDI     Destroyed
;
;               Flags:
;
;               Note:   Interrupts preserved.
;
; Remarks:      This routine is called by the ethernet media module.
;               It can be called at process or interrupt time.
;
; See Also:     ETHERTSM\EtherTSMPromiscuousChange
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
DriverPromiscuousChange proc

        mov     edx, [ebp].ReceiveConfiguration
        mov     al, NIC_Multicast OR NIC_Broadcast
        mov     [ebp].ReceiveStatusTest, 4eh    ; Assume Promiscuous off.
        or      ecx, ecx                        ; Disable Promiscuous?
   je      short DriverPromiscuousModify   ; Jump if so.

   test    ecx , PROM_MODE_RMC
   jnz     Enable_RMC

        or      al, NIC_Promiscuous or NIC_Save_Errors OR NIC_Save_Runts
   mov     [ebp].ReceiveStatusTest, 40h

Enable_RMC:
   mov     ecx, -1

DriverPromiscuousModify:
        out     dx, al

        mov     edx, [ebp].Command              ; Set for page one (62h)
        mov     al, NIC_Page1
        out     dx, al

        mov     al, cl                          ; Set all bits to cl.
        mov     edx, [ebp].MultiCastAddrReg     ; Point at NIC address.

        mov     ecx, 8
SetHashTable:
        out     dx,al                           ; Send it out.
        inc     dx                              ; Go to next register
        loop    SetHashTable

        mov     edx, [ebp].Command              ; Set back to page 0 (22h)
        mov     al,NIC_Page0
        out     dx, al
        ret

DriverPromiscuousChange endp
        subttl  -- DriverCallBack --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( DriverCallBack, NE2000/API/CALLBACK )
;
; Name:         DriverCallBack
;
; Description:  This routine will be executed once every second. It will
;               detect if the hardware does not ack a transmission. If the
;               hardware didn't ack then it will be reset, the transmission
;               of that packet will be aborted and the next packet in the
;               queue will be sent if there is one.
;
; On Entry:     EAX     N/A
;               EBX     @ Frame Data Space
;               ECX     N/A
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     N/A
;               EDI     N/A
;
;               Note:   Interrupts are disabled.
;
; On Return:    EAX     Destroyed
;               EBX     Preserved
;               ECX     Destroyed
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Destroyed
;               EDI     Destroyed
;
;               Flags:
;
;               Note:   Interrupts disabled.
;
; Remarks:      This routine is called by the MSM.
;               After this call returns, the MSM will schedule another
;               call back.
;               It is called at interrupt time.
;
; See Also:     MSM\MSMCallBackProcedure
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
        align   16
DriverCallBack  proc

        mov     ecx, [ebp].TxStartTime          ; ECX -> current Tx ECB.
        or      ecx, ecx                        ; Any transmits active?
        jz      short NoTxActive                ; Jump if not.

        MSMGetCurrentTime                       ; EAX = Current Time.
        sub     eax, ecx                        ; EAX = Current Time - TxStartTime.

        cmp     eax, 36                         ; More than 2 seconds passed?
        jg      short DidntGetTxCompleteInterrupt       ; Process timeout if so.

NoTxActive:
        ret                                     ; Done.
;
;***************************************************************\
;                                                               *
; TransmitBufferEmpty didn't set in time. Reset NIC and try     *
; again.                                                        *
;                                                               *
;***************************************************************/
;
DidntGetTxCompleteInterrupt:

        mov     edx, [ebp].NumberCollisions     ; Find out if transmission
        in      al, dx                          ; experienced any collisions.
        movzx   eax, al                         ; Clear upper bits.
        cmp     eax, 1                          ; Single collision?
        je      short DidntGetTxSingleCol       ; jump if so.
        add     [ebp].TxOKMultipleCollisions, eax       ; Add to statistics.
        jmp     short DidntGetTxReadCom

DidntGetTxSingleCol:
        inc     [ebp].TxOKSingleCollision

DidntGetTxReadCom:
        mov     edx, [ebp].Command              ; Read command port.
        in      al, dx
        test    al, NIC_TxInProgress            ; Transmit still in progress?
        jz      short NotCableDisconnect        ; Jump if not.
;
;***************************************************************\
;                                                               *
; The transmit is still in progress. There is the possibility   *
; that the thin cable has been disconnected. Queue a warning    *
; message to the console.                                       *
;                                                               *
;***************************************************************/
;
        lea     esi, TransmitTimeoutMessage
        call    MSMAlertWarning

NotCableDisconnect:

        mov     eax, OP_SCOPE_ADAPTER
        call    DriverReset                     ; Hard reset the card.
;
;***************************************************************\
;                                                               *
; Forget this packet and go to the next one.                    *
;                                                               *
;***************************************************************/
;
        inc     [ebp].TransmitTimeoutCount      ; Inform diagnostics.
        inc     [ebp].PacketTxMiscErrorCount

        mov     [ebp].TxStartTime, 0            ; Set Tx no longer active.

        mov     edx, [EBP].InterruptStatus      ; Reset all trasmit bits.
        mov     al, 0Ah
        out     dx, al

IF      BackToBackSends
        test    [ebp].TransmitStatusFlag, 0100b ; Another Tx buffer waiting?
        jz      short TimoutCheckQueue          ; Jump if not.
;
;***************************************************************\
;                                                               *
; We just terminated the previous send, but another packet is   *
; already transfered to the board. We just need to initiate the *
; send.                                                         *
;                                                               *
;***************************************************************/
;
        inc     [ebp].BackToBackSendCount       ; Inform diagnostics.

        mov     al, [ebp].TransmitPage0         ; Assume Page #1.
        mov     ecx, [ebp].TransmitSize1        ; Assume Size for Page #1.

        test    [ebp].TransmitStatusFlag, 0010b ; Use page #1?
        jnz     short TimeoutIssueTheSend       ; Jump if so.

        mov     al, [ebp].TransmitPage1         ; Page #2.
        mov     ecx, [ebp].TransmitSize2        ; Size for Page #2.

TimeoutIssueTheSend:

        mov     edx, [ebp].TransmitPage         ; EDX = TxPage register.
        out     dx, al                          ; Send Transmit Page #.

        mov     eax, ecx                        ; EAX = Transmit Size.
        inc     edx                             ; EDX = TxByteCount0 register.
        out     dx, al                          ; Send LSB to register.
        inc     edx                             ; EDX = txByteCount1 register.
        mov     al, ah                          ; AL = MSB.
        out     dx, al                          ; Send MSB to register.

        sub     edx, ITransmitByteCount1 - ICommand
        mov     al, NIC_Transmit                ; Send the Tx Command to
        out     dx, al                          ; the command register.

        MSMGetCurrentTime                       ; EAX = Current Time.
        mov     [ebp].TxStartTime, eax          ; Store it for later.

        xor     [ebp].TransmitStatusFlag, 0110b ; 5->3, 7->1.

        jmp     short TimeoutCheckForMoreSends  ; Try to queue another Tx.

TimoutCheckQueue:
        mov     [ebp].TransmitStatusFlag, 0010b ; No Tx in progress.
                                                ; Buffer #1 is free.
TimeoutCheckForMoreSends:
ENDIF
        test    [ebp].MSMStatusFlags, TXQUEUED  ; Any ECB's waiting.
        jz      short TimeoutReturn             ; Jump if not.

        inc     [ebp].EnqueueSendCount          ; Increment statistics.
        call    EtherTSMGetNextSend             ; Get next send if any.
        jne     short TimeoutReturn             ; Jump if no send

        push    ebp
        push    offset TimeoutSendReturn
        cmp     [ebp].NICRAMSegmentBase, 0      ; Shared RAM?
        jne     DriverSendSharedRAM             ; Jump if so.

        inc     [ebp].EnqueueSendCount          ; Increment statistics. JCP
        jmp     DriverSend

TimeoutSendReturn:
        pop     ebp
TimeoutReturn:
        ret                                     ; Otherwise get out.

DriverCallBack  endp

        subttl  -- DriverSend --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( DriverSend, NE2000/API/SEND )
;
; Name:         DriverSend
;
; Description:  This routine will transfer the packet described in the
;               TCB to the NIC and initiate the send. TxStartTime and
;               RetryCounter must be set to enable the deadman timer.
;
; On Entry:     EAX     N/A
;               EBX     @ Frame Data Space
;               ECX     Padded Packet Length
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     @ TCB
;               EDI     N/A
;
;               Note:   Interrupts are disabled.
;
; On Return:    EAX     Destroyed
;               EBX     Preserved
;               ECX     Destroyed
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Destroyed
;               EDI     Destroyed
;
;               Flags:
;
;               Note:   Interrupts disabled.
;
; Remarks:      This routine is called by the MSM media module.
;               It is called at process or interrupt time.
;
; See Also:     ETHERTSM\EtherTSMDriverSend
;               ETHERTSM\MediaSendRaw8023
;               ETHERTSM\MediaSendEthernetII
;               ETHERTSM\MediaSend8022Over8023
;               ETHERTSM\MediaSend8022Snap
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
        align   16
DriverSend      proc

        mov     edx, [ebp].InterruptStatus              ; Clear DMA complete
        mov     al, 40h                                 ; left by any completed
        out     dx, al                                  ; transmission.

IF      BackToBackSends
        test    [ebp].TransmitStatusFlag, 0010b         ; Use Buffer #1?
        jnz     short IsPageOne                         ; Jump if so.

        mov     [ebp].TransmitSize2, ecx                ; Store for size &
        mov     eax, 4600h                              ;  set remote address
        jmp     short TxSizeSaved                       ;  for page 2.

IsPageOne:
        mov     [ebp].TransmitSize1, ecx                ; Store for size &
        mov     eax, 4000h                              ;  set remote address

                                                        ;  for page 1.
TxSizeSaved:
ELSE
        mov     [ebp].TransmitSize1, ecx                ; Store Size.
        mov     eax, 4000h                              ; EAX = Remote @.
ENDIF

;
;***************************************************************\
;                                                               *
; Set RemoteStartAddress and RemoteByteCount and DMA Write.     *
; EDX = [EBP].InterruptStatus.                                  *
; EAX = Remote Start Address.                                   *
;                                                               *
;***************************************************************/
;
        inc     edx                     ; EDX = RemoteStartAddress0.
        out     dx, al                  ; Write LSB of Remote Address.
        inc     edx                     ; EDX = RemoteStartAddress1.
        mov     al, ah                  ; AL = MSB of Remote Address.
        out     dx, al

        mov     ecx, [esi].TCBDataLen   ; ECX = Exact Packet Length.
        inc     ecx                     ; Evenize it since the NE2000
        and     cl, 0FEh                ; can only handle word I/O.
        mov     eax, ecx                ; EAX = Length.

        inc     edx                     ; EDX = RemoteByteCount0.
        out     dx, al                  ; Write LSB of length.
        inc     edx                     ; EDX = RemoteByteCount1.
        mov     al, ah                  ; AL = MSB of length.
        out     dx, al                  ; Write MSB of length.

        sub     edx, IRemoteByteCount1 - ICommand       ; EDX = Command.
        mov     al, NIC_RemoteDmaWr             ; Command NIC to Write Data
        out     dx, al                          ; to buffer via DMA.
;
;***************************************************************\
;                                                               *
; Set up EDX and EDI here because of the Raw Send jump.         *
;                                                               *
;***************************************************************/
;
        add     edx, INICData - ICommand        ; EDX = NICData.
        mov     edi, esi                        ; EDI -> TCB.
;
;***************************************************************\
;                                                               *
; Copy the Frame header to the NIC.                             *
;                                                               *
;***************************************************************/
;
        mov     ecx, [edi].TCBMediaHeaderLen            ; ECX = Frame Length.
        lea     esi, [edi].TCBMediaHeader               ; ESI-> Frame.

        shr     ecx, 1                                  ; Copy Frame.
 rep    outsw

        jc      SendOddMediaSetup                       ; Jump if extra byte.
;
;***************************************************************\
;                                                               *
; Copy the fragments to the NIC.                                *
;                                                               *
;***************************************************************/
;
        push    ebp                                     ; Save Adapter Base.
        push    edi                                     ; Save TCB.
        mov     edi, [edi].TCBFragStrucPtr              ; EDI -> Fragments.

        mov     ebp, [edi + 0]                  ; EBP = Number of Fragments.
        lea     ebx, [edi + 4]                  ; EBX -> Fragment Descriptor.

        or      ebp, ebp                        ; Any fragments?
        je      EverythingDone                  ; Jump if not.

FragmentOutLoop:
        mov     ecx, [ebx].FragmentLength               ; ECX = Fragment x Size.
        mov     esi, [ebx].FragmentOffset               ; ESI -> Fragment x.

        shr     ecx, 1                                  ; Copy fragment.
 rep    outsw

        jc      short IsAOddLengthFragment              ; Jump if odd fragment.
        add     ebx, SIZE FragmentStructure             ; EBX -> Next Fragment Descriptor.
        dec     ebp                                     ; More fragments?
        jnz     FragmentOutLoop                         ; Yes. Send it.

        jmp     short EverythingDone                    ; Finished all fragments.
;
;***************************************************************\
;                                                               *
; Add last byte of odd fragment to beginning of next fragment.  *
;                                                               *
;***************************************************************/
;
IsAOddLengthFragment:
        lodsb                           ; Load up the odd ball byte into AL.

SkipTheNullFragment:
        add     ebx, SIZE FragmentStructure     ; EBX -> Next Fragment descriptor.
        dec     ebp                             ; Any more fragments?
        jz      short FragmentsAllDone          ; Jump if not.

        mov     ecx, [ebx].FragmentLength       ; ECX = Next fragment size.
        or      ecx, ecx                        ; Null fragment?
        jz      SkipTheNullFragment             ; Jump if so.
        mov     esi, [ebx].FragmentOffset       ; ESI -> Next fragment.

        mov     ah, al                          ; AH = odd ball byte.
;
;***************************************************************\
;                                                               *
; Enter here if AH contains byte to get added to fragment       *
; output.                                                       *
;                                                               *
;***************************************************************/
;
SendOddMediaEntry:
        or      ecx, ecx                ; Check fragment size.
        lodsb                           ; Pick up first byte of fragment.
        xchg    al, ah                  ; Swap it with odd ball byte.
        out     dx, ax                  ; Send both to NIC.
        jz      short SendOddNext       ; Jump if next fragment zero.
        dec     ecx                     ; Adjust fragment count.
;
;***************************************************************\
;                                                               *
; Odd byte from last fragment and first byte of current fragment*
; have been sent to NIC. Now send rest of fragment normally.    *
;                                                               *
;***************************************************************/
;
        shr     ecx, 1                          ; Send out rest of
 rep    outsw                                   ; fragment.

        jc      IsAOddLengthFragment            ; Jump if it was odd.

SendOddNext:
        add     ebx, SIZE FragmentStructure     ; EBX -> Next fragment descriptor.
        dec     ebp                             ; Any more fragments.
        jnz     FragmentOutLoop                 ; Yes. Send it.
        jmp     short EverythingDone            ; No. All done.
;
;***************************************************************\
;                                                               *
; Enter here if AL contains odd byte with no more fragments to  *
; follow.                                                       *
;                                                               *
;***************************************************************/
;
FragmentsAllDone:

        out     dx, ax                  ; Put out the last byte plus garbage.

EverythingDone:
;
;***************************************************************\
;                                                               *
; All fragments have been sent to NIC.                          *
;                                                               *
;***************************************************************/
;
        pop     esi                     ; Restore TCB into ESI.
        pop     ebp                     ; Restore Virtual Adapter base.

        sub     edx, INICData - IInterruptStatus        ; EDX = Interrupt Status.
        in      al, dx                          ; AL = Interrupt Status.
        test    al, 40h                         ; Tx Complete?
        jz      short TestDMAAgain              ; No. Wait for it.
DMAOK:

IF      BackToBackSends

        test    [ebp].TransmitStatusFlag, 0001b ; Waiting for Tx Complete?
        jnz     short DontIssueSend             ; Jump if so.
ENDIF
;
;***************************************************************\
;                                                               *
; Initiate the transmit.                                        *
;                                                               *
;***************************************************************/
;
        mov     edx, [ebp].TransmitPage         ; Make sure we Tx buffer #1.
IF      BackToBackSends
        mov     al, [ebp].TransmitPage0         ; JCP, 941108.
        out     dx, al
ENDIF
        mov     eax, [ebp].TransmitSize1        ; EAX = Tx size for Buffer #1.
        inc     edx                             ; EDX = TxByteCount0.

        out     dx, al                  ; Send LSB of Tx Byte Count.
        inc     edx                     ; EDX = TxByteCount1.
        mov     al, ah                  ; AL = MSB of Tx Byte Count.
        out     dx, al                  ; Send MSB of Tx Byte Count.

        sub     edx, ITransmitByteCount1 - ICommand     ; EDX = Command.
        mov     al, NIC_Transmit                        ; Send packet.
        out     dx, al

        MSMGetCurrentTime                       ; EAX = Current Time.
        mov     [ebp].TxStartTime, eax          ; Store it for later.
        mov     [ebp].RetryCounter, MaxRetries  ;  MAX retries.
IF      BackToBackSends
        mov     [ebp].TransmitStatusFlag, 0001b ; Sending Page 1 status.
ENDIF
;
;***************************************************************\
;                                                               *
; Give TCB back to MSM. (Lying Send)                            *
;                                                               *
;***************************************************************/
;
IF NOT  UseFastCalls
        cmp     [ebp].InDriverISR, 0            ; In DriverISR?
        jnz     EtherTSMSendComplete            ; Jump if so.
ENDIF
        jmp     EtherTSMFastSendComplete        ; Otherwise service events.

IF      BackToBackSends
DontIssueSend:
        or      [ebp].TransmitStatusFlag, 0100b ; Set 2nd send buffer waiting.
IF NOT  UseFastCalls
        cmp     [ebp].InDriverISR, 0            ; In DriverISR?
        jnz     EtherTSMSendComplete            ; Jump if so.
ENDIF
        jmp     EtherTSMFastSendComplete        ; Otherwise service events.
ENDIF
;
;***************************************************************\
;                                                               *
; Send last header byte with first fragment.                    *
;                                                               *
;***************************************************************/
;
SendOddMediaSetup:
        mov     ah, [esi]                       ; esi -> Last byte of header.
        push    ebp                             ; Save Adapter Base.
        push    edi                             ; Save TCB.
        mov     edi, [edi].TCBFragStrucPtr      ; EDI -> Fragments.

        mov     ebp, [edi + 0]                  ; EBP = Number of Fragments.
        lea     ebx, [edi + 4]                  ; EBX -> Fragment Descriptor.

        mov     ecx, [ebx].FragmentLength       ; ECX = Fragment 0 length.
        mov     esi, [ebx].FragmentOffset       ; ESI -> Fragment 0.
;
;***************************************************************\
;                                                               *
; Start sending fragments with AH = last byte of the header.    *
;                                                               *
;***************************************************************/
;
        jmp     SendOddMediaEntry
;
;***************************************************************\
;                                                               *
; Control is transfered here if DMA complete bit hasn't set.    *
;                                                               *
;***************************************************************/
;
TestDMAAgain:
        mov     ecx, 10000h                     ; ECX = Loop counter.
TryItAgain:
        in      al, dx                          ; AL = Interrupt Status.
        test    al, 40h                         ; Tx Complete?
        jnz     DMAOK
        dec     ecx
        jnz     TryItAgain                      ; No. Wait some more.
;
;***************************************************************\
;                                                               *
; Tx DMA complete wouldn't set. Manufacture claims it really    *
; was so we'll reset it manually, display message and continue. *
;                                                               *
;***************************************************************/
;
        mov     al, 40h                         ; Clear the interrupt
        out     dx, al                          ; DMA complete bit.

        push    esi
        lea     esi, DMANotCompleteError
        call    MSMAlertWarning

        mov     eax, OP_SCOPE_ADAPTER
        call    DriverReset                     ; Reset the NIC.
        pop     esi
        jmp     DriverSend                      ; Try sending again.

DriverSend      endp

        align   16
DriverSendSharedRAM     proc

        mov     edi, [ebp].NICRAMSegmentBase    ; Load Tx segment for page #1.

if BackToBackSends
        test    [ebp].TransmitStatusFlag, 0010b         ; Use Buffer #1?
        jnz     short RAMIsPageOne                      ; Jump if so.

        mov     [ebp].TransmitSize2, ecx                ; Store size & adjust
        add     edi, 600h                               ;  Tx segment
        jmp     short RAMTxSizeSaved                    ;  for page 2.

RAMIsPageOne:
        mov     [ebp].TransmitSize1, ecx                ; Store size
                                                        ;  for page 1.
RAMTxSizeSaved:
else
        mov     [ebp].TransmitSize1, ecx
endif
        mov     edx, esi                                ; EDX -> TCB.
;
;***************************************************************\
;                                                               *
; Copy the Frame header to the NIC.                             *
;                                                               *
;***************************************************************/
;
        mov     ecx, [edx].TCBMediaHeaderLen    ; ECX = Media Header size.
        lea     esi, [edx].TCBMediaHeader       ; ESI -> Media Header.

        shr     ecx, 2                          ; ECX = Media size / 4.
 rep    movsd                                   ; Copy Header in DWORDs.

        mov     ecx, [edx].TCBMediaHeaderLen    ; ECX = Media size && 3.
        and     ecx, 3
 rep    movsb                                   ; Copy remaining bytes.
;
;***************************************************************\
;                                                               *
; Copy the Fragments to the NIC.                                *
;                                                               *
;***************************************************************/
;
        mov     esi, [edx].TCBFragStrucPtr      ; ESI -> Fragment Count.
        mov     eax, [esi + 0]                  ; EAX = Fragment Count.
        lea     ebx, [esi + 4]                  ; EBX -> First Fragment struc.

        or      eax, eax                        ; Any Fragments?
        je      short RAMEverythingDone         ; Jump if not.

RAMFragmentOutLoop:
        mov     ecx, [ebx].FragmentLength       ; ECX = Fragment x Size.
        mov     esi, [ebx].FragmentOffset       ; ESI -> Fragment x.

        cmp     ecx, 4                          ; Less than four bytes?
        jb      short RAMDontAlignMidgets       ; Skip if so.
        test    edi, 1                          ; Off by a byte?
        jz      short RAMDontAdjustByte         ; Jump if not.
        movsb                                   ; Take off of byte alignment.
        dec     ecx                             ; Adjust counter.
RAMDontAdjustByte:
        test    edi, 2                          ; Off by a word?
        jz      short RAMDontAdjustWord         ; Jump if not.
        movsw                                   ; Take off of word alignment.
        sub     ecx, 2                          ; Adjust counter.
RAMDontAdjustWord:
RAMDontAlignMidgets:
        push    ecx                             ; Save Length

        shr     ecx, 2                          ; Copy fragment in DWORDs.
 rep    movsd

        pop     ecx

        and     ecx, 3                          ;  & 3.
 rep    movsb                                   ; Copy remaining bytes.

        add     ebx, SIZE FragmentStructure     ; EBX -> Next Fragment Descriptor.
        dec     eax                             ;
        jnz     RAMFragmentOutLoop              ; Yes. Send it.
;
;***************************************************************\
;                                                               *
; All fragments have been sent to NIC.                          *
;                                                               *
;***************************************************************/
;
RAMEverythingDone:
        mov     esi, edx                        ; ESI -> TCB.

if BackToBackSends
        test    [ebp].TransmitStatusFlag, 0001b ; Waiting for Tx Complete?
        jnz     short RAMDontIssueSend          ; Jump if so.
endif
;
;***************************************************************\
;                                                               *
; Initiate the transmit.                                        *
;                                                               *
;***************************************************************/
;
        mov     edx, [ebp].TransmitPage         ; Make sure we Tx buffer #1.
        mov     al, 0h
        out     dx, al

        mov     eax, [ebp].TransmitSize1        ; EAX = Tx size for Buffer #1.
        inc     edx                             ; EDX = TxByteCount0.

        out     dx, al                          ; Send LSB of Tx Byte Count.
        inc     edx                             ; EDX = TxByteCount1.
        mov     al, ah                          ; AL = MSB of Tx Byte Count.
        out     dx, al                          ; Send MSB of Tx Byte Count.

        sub     edx, ITransmitByteCount1 - ICommand     ; EDX = Command.
        mov     al, NIC_Transmit                        ; Send packet.
        out     dx, al

        MSMGetCurrentTime                       ; EAX = Current Time.
        mov     [ebp].TxStartTime, eax          ; Store it for later.

        mov     [ebp].RetryCounter, MaxRetries  ; Initialize retry counter.

if BackToBackSends
        mov     [ebp].TransmitStatusFlag, 0001b ; Sending Page 1 status.
endif
;
;***************************************************************\
;                                                               *
; Give TCB back to LSL. (Lying Send)                            *
;                                                               *
;***************************************************************/
;
IF NOT  UseFastCalls
        cmp     [ebp].InDriverISR, 0            ; In DriverISR?
        jnz     EtherTSMSendComplete            ; Jump if so.
ENDIF
        jmp     EtherTSMFastSendComplete        ; Otherwise service events.

RAMDontIssueSend:

        or      [ebp].TransmitStatusFlag, 0100b ; Set 2nd send buffer waiting.
IF NOT  UseFastCalls
        cmp     [ebp].InDriverISR, 0            ; In DriverISR?
        jnz     EtherTSMSendComplete            ; Jump if so.
ENDIF
        jmp     EtherTSMFastSendComplete        ; Otherwise service events.

DriverSendSharedRAM     endp

        subttl  -- DriverISR --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( DriverISR, NE2000/API/ISR )
;
; Name:         DriverISR
;
; Description:  This routine handles packet reception and transmit complete
;               interrupts.
;
; On Entry:     EAX     N/A
;               EBX     N/A
;               ECX     N/A
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     N/A
;               EDI     N/A
;
;               Note:   Interrupts are disabled.
;
; On Return:    EAX     Destroyed
;               EBX     Destroyed
;               ECX     Destroyed
;               EDX     Destroyed
;               EBP     Destroyed
;               ESI     Destroyed
;               EDI     Destroyed
;
;               Flags:
;
;               Note:   Interrupts disabled.
;
; Remarks:      This routine is called by the MSM.
;               It is called at interrupt time.
;
; See Also:     MSM\MSMInterruptProcedure
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
PagingError:
;
;***************************************************************\
;                                                               *
; The receive buffers have been corrupted. Reset the NIC.       *
;                                                               *
;***************************************************************/
;
        inc     [ebp].PacketRxMiscErrorCount
        inc     [ebp].RxPagingErrorCount        ; Inform diagnostics.

        mov     eax, OP_SCOPE_ADAPTER
        call    DriverReset                     ; Re-initialize card.
        jmp     GetOut                          ; Leave ISR.


        align   16
DriverISR       proc
IF NOT  UseFastCalls
        inc     [ebp].InDriverISR               ; Set for DriverSend.
ENDIF
;JCJ 04-July-1997 SPD#158389 IODetachBit checking is removed to support
;                                                       partial shutdown
        test    [ebp].MSMStatusFlags, SHUTDOWN   ; Complete Shutdown?   
;JCJ 04-July-1997 End

        jne     ClearIntsAndGetOut                      ; Jump if so.
;
;***************************************************************\
;                                                               *
; ISR will branch and keep returning here until card is fully   *
; serviced.                                                     *
;                                                               *
;***************************************************************/
;
PollAgain:
   cmp   InDriverInit, 0         ;SPD 125034
   jne   ClearIntsAndGetOut      ;SPD 125034

        mov     edx, [ebp].InterruptStatus      ; Read the NIC ISR.
        in      al,dx                           ; AL and
        mov     ah, al                          ; AH contain Interrupt Status.
        test    al, NIC_PacketReady     ; Receiver needs servicing(15h)?
        jz      CheckForTransmit        ; No. Check for Transmit Complete.
;
;***************************************************************\
;                                                               *
; Receive status bits set.                                      *
;       01h - Packet Received with no error.                    *
;       04h - Receive Error.                                    *
;       10h - Receive Buffer Overflow.                          *
;                                                               *
;***************************************************************/
;
        test    ah, NIC_RecvError       ; Was it a successful read(14h)?
        jnz     HandleRxErrors          ; Jump if not.

        sub     edx, IInterruptStatus - ICommand        ; EDX = Command.
        mov     al, NIC_Page1                   ; Switch to page 1 registers.
        out     dx, al

        add     edx, IInterruptStatus - ICommand        ; EDX = Current.
        in      al, dx                          ; AL = NIC's current page.
        cmp     al, [ebp].NextPage              ; Any more Rx buffers?

        mov     edx, [ebp].Command              ; Switch to page 0 registers
        mov     al, NIC_Page0                   ; before jumping.
        out     dx, al
        jz      DidntGetAnything                ; No. Done receiving.
;
;***************************************************************\
;                                                               *
; Time to read in the packet received.                          *
;                                                               *
; Read in 4 byte buffers header, 14 byte Ethernet header and    *
; following 8 bytes into ReceiveHeader to properly analyse      *
; packet length and media type.                                 *
;                                                               *
;***************************************************************/
;
ReceiveNextPacket:
        add     edx, IInterruptStatus - ICommand        ; EDX = Int status.
        mov     al, 1                           ; Reset the RX Bit.
        out     dx, al
;
;***************************************************************\
;                                                               *
; Set up the REMOTE START ADDDRESS of NIC.                      *
;                                                               *
;***************************************************************/
;
        cmp     [ebp].NICRAMSegmentBase, 0      ; Shared RAM?
        jne     DriverISRSharedRAM              ; Jump if so.

        mov     edx, [ebp].RemoteStartAddress0  ; EDX = RemoteStartAddress0.
        xor     al, al                          ; Zero out lower byte.
        out     dx, al

        inc     edx                             ; EDX = RemoteStartAddress1.
        mov     al, [ebp].NextPage              ; AL = Next_page.
        out     dx, al                          ; Set to read next page.
;
;***************************************************************\
;                                                               *
; Set up the REMOTE BYTE COUNT.                                 *
;                                                               *
;***************************************************************/
;
        inc     edx                             ; EDX = RemoteByteCount0.
        mov     al, byte ptr [ebp].MSMMaxFrameHeaderSize        ; AL = Header Size.
        add     al, 5                           ; Add 4-byte 8390 header
        and     al, 0feh                        ;  and evenize it.
        out     dx, al                          ; Send Size (LO).

        movzx   ecx, al                         ; Save count for later.

        inc     edx                             ; EDX = RemoteByteCount1.
        xor     al, al
        out     dx, al                          ; Send Size (HI).
;
;***************************************************************\
;                                                               *
;  Issue the Remote Read DMA command to the NIC.                *
;                                                               *
;***************************************************************/
;
        sub     edx, IRemoteByteCount1 - ICommand       ; EDX = Command.
        mov     al, NIC_RemoteDmaRd
        out     dx, al                          ; Command NIC to Read from memory.

        add     edx, INICData - ICommand        ; EDX = NICData.
        lea     edi, [ebp].ReceiveHeader        ; EDI -> Where to read data int0.

        shr     ecx, 1                          ; Convert count to words.

  rep   insw                                    ; Read Header and Frame info.

        mov     al, [ebp].ReceiveStatusTest
        test    [ebp].ReceiveHeader.RReceiveStatus, al  ; Rx error?
        jnz     PagingError                             ; Jump if so.
        movzx   ecx, [ebp].ReceiveHeader.RByteCount     ; ECX = Packet Length.

        movzx   eax, [ebp].ReceiveHeader.RNextBuffer    ; AL = Next Page.
        cmp     al, [ebp].NextPage                      ; Did Next Page wrap?
        jae     short CheckForLong                      ; Jump if not.
        add     al, PSTOP - PSTART                      ; Set AL higher.
CheckForLong:
        sub     al, [ebp].NextPage                      ; AL = Number of Pages.
        cmp     al, 8                                   ; > 1792?
        ja      PacketTooLong                           ; Jump if so.
PacketTooLongReturn:
        mov     eax, ecx                                ; EAX = PacketLength.
;
;***************************************************************\
;                                                               *
; Use ByteCount and NextPage to determine any Paging errors.    *
;                                                               *
;***************************************************************/
;
        add     eax, 4 - 1 + 100h       ; (4 - 1 + 100h) where the last byte
        add     ah, [ebp].NextPage      ;  was plus round up.

        cmp     ah, PSTOP               ; Wrap to beginning of ring?
        jb      short NoOverflow        ; No. Jump.

        add     ah, PSTART - PSTOP      ; Adjust to front of ring.

NoOverflow:
        cmp     [ebp].ReceiveHeader.RNextBuffer, ah     ; Does header agree?
        jnz     PagingError                     ; No. Paging error.
        mov     [ebp].NextPage, ah              ; Save new NextPage.
;
;***************************************************************\
;                                                               *
; Now let the MSM decide if we should accept the packet.        *
;                                                               *
;***************************************************************/
;
        lea     esi, [ebp].ReceiveHeader.ReceiveHeaderStructure
        sub     ecx, SizeOfCRCField             ; Subtract CRC length.

        movzx   eax, [ebp].ReceiveHeader.RReceiveStatus
        shr     al, 1
        and     al, 03h                         ; Save CRC error bits.

        call    EtherTSMGetRCB                  ; Get an ECB.
        jnz     PacketNotReceived               ; Jump if error.
   or   ecx, ecx         ;
   jz   ReturnRCBToMSM         ; Jump if nothing to copy
;
;***************************************************************\
;                                                               *
; ESI -> ECB.                                                   *
; EDI -> Place in ECB to resume reading in data from NIC.       *
; ECX =  Bytes remaining to be read from NIC.                   *
; EBX =  Bytes in packet to skip over.                          *
;                                                               *
;***************************************************************/
;
        mov     [ebp].TotalBytes, ecx           ; Save UN-EVENIZED for later.
        mov     [ebp].OddValue, 0               ; clear odd byte flag.
        inc     ecx                             ; prepare for evenization.

        add     ebx, SIZE ReceiveHeaderStructure ; EBX = Bytes to skip + 4.
        test    ebx, 1                          ; Even offset?
        jz      short SkipValueEven             ; Jump if so.

        mov     al, byte ptr [ebp + ebx].ReceiveHeader
        mov     [ebp].OddByteValue, al          ; copy already i/o'd byte
        mov     [ebp].OddValue, 1               ; tell loop to copy this loner.
        dec     ecx                             ; already got byte
        inc     ebx                             ; already got byte
SkipValueEven:
        mov     edx, [ebp].RemoteStartAddress0  ; EDX = RemoteStartAddress0.
        mov     al, bl                          ; BL = Bytes to skip.
        out     dx, al
        and     cl, 0FEh
        mov     eax, ecx                        ; EAX = evenized length.
        inc     edx                             ; EDX = RemoteStartAddress1.
        inc     edx                             ; EDX = RemoteByteCount0.
        out     dx, al                          ; Send LSB of Count.
        inc     edx                             ; EDX = RemoteByteCount1.
        mov     al, ah                          ; AL = MSB of Count.
        out     dx, al                          ; Send MSB of Count.
;
;***************************************************************\
;                                                               *
; Issue the remote read DMA command to the card.                *
;                                                               *
;***************************************************************/
;
        sub     edx, IRemoteByteCount1 - ICommand       ; EDX = Command.
        mov     al, NIC_RemoteDmaRd             ; Remote read DMA.
        out     dx, al
        add     edx, INICData - ICommand        ; EDX = NICData.
;
;***************************************************************\
;                                                               *
; Copy data into ECB packet data.                               *
;                                                               *
;***************************************************************/
;
        mov     ebx, edi                        ; EBX -> Frag Count.
        mov     eax, [ebx]                      ; EAX = Frag Count.
        mov     [ebp].RxFragments, eax          ; Save fragment count.
        add     ebx, 4                          ; EBX -> Frag structs.

ReadNextFragmentStructure:
        mov     edi, [ebx+0]                    ; EDI -> Frag offset.
        mov     ecx, [ebx+4]                    ; ECX = Frag size.
        jecxz   MoveWasEven
        sub     [ebp].TotalBytes, ecx           ; adjust total remaining
        jnc     UseOneFromRCB
        add     ecx, [ebp].TotalBytes           ; ECX = bytes left.
        mov     [ebp].TotalBytes, 0             ; Don't read any more.
UseOneFromRCB:
        cmp     [ebp].OddValue, 0               ; check if odd value
        jz      ReadNextFragment

        mov     al, [ebp].OddByteValue          ; Get odd value
        stosb                                   ; copy it into RCB frag
        dec     ecx                             ; adjust count
        mov     [ebp].OddValue, 0               ; clear flag
ReadNextFragment:
        shr     ecx, 1                          ; Divide by 2.
        rep     insw                            ; Read words.
        jnc     MoveWasEven                     ; Jump if extra byte left.

        in      ax, dx                          ; AL = Next Byte.
        stosb                                   ; Store into fragment.
        mov     [ebp].OddByteValue, ah          ; save odd value
        mov     [ebp].OddValue, 1               ; set flag
MoveWasEven:
        cmp     [ebp].TotalBytes, 0             ; More bytes to read?
        je      short ReturnRCBToMSM            ; Jump out if so.
        add     ebx, 8                          ; EBX -> Next Frag struct.
        dec     [ebp].RxFragments               ; Decrement fragment count.
        jne     ReadNextFragmentStructure       ; Jump if more frags.
ReturnRCBToMSM:
IF      UseFastCalls
        push    ebp
        call    EtherTSMFastRcvComplete         ; Give ECB to protocol stack.
        pop     ebp
ELSE
        call    EtherTSMRcvComplete             ; Give ECB back to LSL.
ENDIF
;
;***************************************************************\
;                                                               *
; Update Boundry register here in case the NIC is inserting     *
; packets into the ring while we are reading them.              *
;                                                               *
;***************************************************************/
;
FinishUp:
        mov     al, [ebp].NextPage              ; AL = Page of next buffer.
        mov     ah, al                          ; Save for TestForReceive.

        dec     al                              ; Boundary = NextPage - 1.
        cmp     al, PSTART                      ; Still within Rx boundary.
        jae     short LoadNewBoundary           ; Yes. Use it.

        mov     al, PSTOP - 1                   ; No. Move to end of ring.

LoadNewBoundary:
        mov     edx, [ebp].Boundary
        out     dx, al                          ; BOUNDARY <- NextPage ptr - 1
;
;***************************************************************\
;                                                               *
; Check to see if more packets are waiting to be received.      *
;                                                               *
;***************************************************************/
;
TestForReceive:
        mov     edx, [ebp].Command      ; EDX = Command.
        mov     al, NIC_Page1           ; Go into page 1 registers.
        out     dx, al

        add     edx, IInterruptStatus - ICommand        ; EDX = Current.
        in      al, dx
        cmp     al, ah                  ; Any more Rx buffers?

        mov     edx, [ebp].Command      ; Return to page 0 registers
        mov     al, NIC_Page0           ; before jumping.
        out     dx, al
        jnz     ReceiveNextPacket       ; Yes. Get next packet.
;
;***************************************************************\
;                                                               *
; Now see if we're in an Overflow loopback mode.                *
;                                                               *
;***************************************************************/
;
        cmp     [ebp].OverflowRestartFlag, 0    ; In overflow mode?
        jz      PollAgain                       ; No. Poll status port again.
;
;***************************************************************\
;                                                               *
; Now that receive buffers are empty, we can finish handling    *
; the Buffer Overflow error by placing the NIC out of the       *
; loopback mode and sending out the current Tx buffer if it had *
; been active at the time of overflow and got cancelled by the  *
; stop command.                                                 *
;                                                               *
;***************************************************************/
;
        mov     edx, [ebp].TransmitConfiguration
        xor     al, al                          ; Take card out of loopback.
        out     dx, al
        mov     [ebp].OverflowRestartFlag, al

        cmp     [ebp].RetryTxFlag, al           ; Restart Tx flag?
        je      PollAgain                       ; Finished if not.
;
;***************************************************************\
;                                                               *
; A transmit was cancelled during overflow condition.           *
; Re-send it.                                                   *
;                                                               *
;***************************************************************/
;
        mov     [ebp].RetryTxFlag, al           ; Clear restart flag.

IF      BackToBackSends
        mov     al, [ebp].TransmitPage0         ; Assume Page #1.
        mov     ecx, [ebp].TransmitSize1        ; Assume size for Page #1.

        test    [ebp].TransmitStatusFlag, 0010b ; Use Page #1?
        jz      short RestartIssueTheSend       ; Jump if so.

;JCP    mov     al, TransmitPage1               ; Set Page #2.
        mov     al, [ebp].TransmitPage1         ; Set Page #2. JCP, 941108.
        mov     ecx, [ebp].TransmitSize2        ; Set size for Page #2.

RestartIssueTheSend:

        mov     edx, [ebp].TransmitPage         ; EDX = TxPage register.
        out     dx, al                          ; Set Page register.

        inc     edx                             ; EDX = TxCount0 register.
        mov     eax, ecx                        ; EAX = Transmit size.
ELSE
        mov     edx, [ebp].TransmitByteCount0
        mov     eax, [ebp].TransmitSize1
ENDIF
        out     dx, al                          ; Send LSB of TxByteCount.
        inc     edx                             ; EDX = TxCount1 register.
        mov     al, ah                          ; AL = MSB of TxByteCount.
        out     dx, al                          ; Send MSB of TxByteCount.

        sub     edx, ITransmitByteCount1 - ICommand     ; EDX = Command port.
        mov     al, NIC_Transmit                ; Issue the Tx command.
        out     dx, al

        MSMGetCurrentTime                       ; EAX = Current Time.
        mov     [ebp].TxStartTime, eax          ; Store for later.

        mov     [ebp].RetryCounter, MaxRetries  ;  Page #2.

        jmp     PollAgain                       ; Return.

PacketTooLong:
        dec     al
        shr     al, 3                           ; AL = (AL - 1) / 8.
        shl     eax, 11                         ; EAX = EAX * 2048.
        add     ecx, eax                        ; Add to total count.
        jmp     PacketTooLongReturn

SharedPacketTooLong:
        dec     al
        shr     al, 3                           ; AL = (AL - 1) / 8.
        shl     eax, 11                         ; EAX = EAX * 2048.
        add     ecx, eax                        ; Add to total count.
        jmp     short SharedPacketTooLongReturn
;
;***************************************************************\
;                                                               *
; Read packet from shared RAM adapter.                          *
;                                                               *
;***************************************************************/
;
DriverISRSharedRAM:
;
;***************************************************************\
;                                                               *
; Read in Receive Header from next packet in the ring.          *
;                                                               *
;***************************************************************/
;
        xor     eax, eax                        ; Prepare EAX to point
        mov     ah, [ebp].NextPage              ; to the next Rx page.
        add     eax, [ebp].NICRAMSegmentBase    ; Add Shared mem offset.
        mov     esi, eax                        ; ESI->Next Rx Packet.

        mov     al, [ebp].ReceiveStatusTest
        test    [esi].RReceiveStatus, al        ; Rx error?
        jnz     PagingError                     ; Jump if so.

        movzx   ecx, [esi].RByteCount           ; ECX = Packet Length.

        movzx   eax, [esi].RNextBuffer                  ; AL = Next Page.
        cmp     al, [ebp].NextPage                      ; Did Next Page wrap?
        jae     short SharedCheckForLong                ; Jump if not.
        add     al, PSTOP - PSTART                      ; Set AL higher.
SharedCheckForLong:
        sub     al, [ebp].NextPage                      ; AL = Number of Pages.
        cmp     al, 8                                   ; > 1792?
        ja      SharedPacketTooLong                     ; Jump if so.
SharedPacketTooLongReturn:
        mov     eax, ecx                                ; EAX = PacketLength.
;
;***************************************************************\
;                                                               *
; Use ByteCount and NextPage to determine any Paging errors.    *
;                                                               *
;***************************************************************/
;
        add     eax, 4 - 1 + 100h       ; (4 - 1 + 100h) where the last byte
        add     ah, [ebp].NextPage      ; was plus round up.

        cmp     ah, RAMPSTOP            ; Wrap to beginning of ring?
        jb      short RAMNoOverflow     ; No. Jump.

        add     ah, RAMPSTART - RAMPSTOP        ; Adjust to front of ring.

RAMNoOverflow:
        cmp     ah, [esi].RNextBuffer   ; Does header agree?
        jnz     PagingError             ; No. Paging error.
        mov     [ebp].NextPage, ah                      ; Save for next time.
;
;***************************************************************\
;                                                               *
; Now let the MSM decide if we should accept the packet.        *
;                                                               *
;***************************************************************/
;
        movzx   eax, [esi].RReceiveStatus
        shr     al, 1
        and     al, 03h                            ; Save CRC error bits.
        sub     ecx, SizeOfCRCField                ; Subtract CRC length.
        add     esi, SIZE ReceiveHeaderStructure   ; ESI -> Dest addr.
        mov     [ebp].CurrentPacketPointer, esi    ; Save position.
        call    EtherTSMGetRCB                     ; Get an ECB.
        jnz     PacketNotReceived                  ; Jump if error.
   or   ecx, ecx         
   jz   RxReturnECB            ; Jump if nothing to copy
                     
        mov     [ebp].CurrentECB, esi              ; Save ECB.
;
;***************************************************************\
;                                                               *
; EDX -> ECB.                                                   *
; EDI -> Place in ECB to resume reading in data from NIC.       *
; ECX =  Bytes remaining to be read from NIC.                   *
; EBX =  Bytes in packet to skip over.                          *
;                                                               *
;***************************************************************/
;
        mov     esi, [ebp].CurrentPacketPointer ; ESI -> Next Rx Data to read.
        add     esi, ebx                        ; Add bytes to skip.
;
;***************************************************************\
;                                                               *
; Copy the rest of the packet into ECB packet data.             *
;                                                               *
;***************************************************************/
;
        mov     [ebp].TotalBytes, ecx           ; Save for later.
        mov     ebx, edi                        ; EBX -> Frag Count.
        mov     eax, [ebx]                      ; EAX = Frag Count.
        add     ebx, 4                          ; EBX -> 1st Frag Structure.

        mov     edx, [ebp].NICRAMSegmentLimit   ; EDX = End of RAM.
        sub     edx, esi                        ; EDX = EDX - Our current pos.
        cmp     ecx, edx                        ; Are we going to go over?
        jg      AdjustToRingTop                 ; Jump if so.

RxFragmentLoop:
        mov     edi, [ebx+0]                    ; EDI -> Frag offset.
        mov     ecx, [ebx+4]                    ; ECX =  Frag size.
        sub     [ebp].TotalBytes, ecx
        js      SharedReadNextFragOverrun
SharedReadNextFragment:
        shr     ecx, 2                          ; Copy dwords.
        rep     movsd
        mov     ecx, [ebx+4]                    ; ECX = Frag size.
        and     ecx, 3                          ; Copy leftover bytes.
        rep     movsb

RxNextFragment:
        cmp     [ebp].TotalBytes, 0             ; More bytes to read?
        je      short RxReturnECB               ; Jump out if so.
        add     ebx, 8                          ; EBX -> Next Frag struct.
        dec     eax                             ; More fragments?
        jne     RxFragmentLoop                  ; Jump if not.

RxReturnECB:
        mov     esi, [ebp].CurrentECB           ; ESI -> ECB.
IF      UseFastCalls
        push    ebp
        call    EtherTSMFastRcvComplete         ; Give ECB to protocol stack.
        pop     ebp
ELSE
        call    EtherTSMRcvComplete             ; Give ECB back to LSL.
ENDIF
RAMFinishUp:
        mov     al, [ebp].NextPage              ; AL = Page of next buffer.
        mov     ah, al                          ; Save for TestForReceive.

        dec     al                              ; Boundary = NextPage - 1.
        cmp     al, RAMPSTART                   ; Still within Rx boundary.
        jae     short RAMLoadNewBoundary        ; Yes. Use it.

        mov     al, RAMPSTOP - 1                ; No. Move to end of ring.

RAMLoadNewBoundary:
        mov     edx, [ebp].Boundary
        out     dx, al                          ; BOUNDARY <- NextPage ptr - 1
        jmp     TestForReceive

SharedReadNextFragOverrun:
        add     ecx, [ebp].TotalBytes           ; ECX = bytes left.
        rep     movsb                           ; Copy remaining bytes.
        jmp     RxReturnECB                     ; We're finished.

AdjustToRingTop:
        mov     edi, [ebx+0]                    ; EDI -> Frag offset.
        mov     ecx, [ebx+4]                    ; ECX =  Frag size.
        cmp     ecx, edx                        ; Small fragment?
        jb      short CantReachRingTop          ; Jump if so.
        mov     ecx, edx                        ; ECX = bytes left.
        sub     [ebp].TotalBytes, ecx
        js      SharedReadNextFragOverrun

        shr     ecx, 2                          ; Copy dwords.
        rep     movsd
        mov     ecx, edx                        ; ECX = bytes left.
        and     ecx, 3                          ; Copy leftover bytes.
        rep     movsb

        mov     ecx, [ebx+4]                    ; ECX = Frag size.
        sub     ecx, edx                        ; Subtract what we've copied.
        mov     esi, [ebp].NICRAMReceiveRingStart
        sub     [ebp].TotalBytes, ecx
        js      SharedReadNextFragOverrun

        shr     ecx, 2                          ; Copy dwords.
        rep     movsd
        mov     ecx, [ebx+4]                    ; ECX = Frag size.
        sub     ecx, edx                        ; Subtract what we've copied.
        and     ecx, 3                          ; Copy leftover bytes.
        rep     movsb

        jmp     RxNextFragment

CantReachRingTop:
        sub     [ebp].TotalBytes, ecx
        js      SharedReadNextFragOverrun
        sub     edx, ecx                        ; EDX = New length to end.
        shr     ecx, 2                          ; Copy dwords.
        rep     movsd
        mov     ecx, [ebx+4]                    ; ECX = Frag size.
        and     ecx, 3                          ; Copy leftover bytes.
        rep     movsb
        add     ebx, 8                          ; EBX -> Next Frag structure.
        dec     eax                             ; Any more fragments?
        jne     AdjustToRingTop                 ; Jump if so.
        jmp     RxReturnECB                     ; Jump if not.
;
;***************************************************************\
;                                                               *
; Unable to receive packet. Increment proper statistic counter. *
;                                                               *
;***************************************************************/
;
PacketNotReceived:
        dec     eax                             ; Was EAX == 1?
        jnz     short BadMulticastError         ; Jump if not.
        inc     [ebp].UnsupportedFrameCount     ; Update stat counter.
        jmp     RxFinishUp                      ; Skip receiving this packet.
BadMulticastError:
        dec     eax                             ; Was EAX == 2?
        jnz     RxFinishUp                      ; Jump if not.
        inc     [ebp].UnsupportedMulticastCount ; Update stat counter.
RxFinishUp:
        cmp     [ebp].NICRAMSegmentBase, 0      ; Shared RAM?
        jne     RAMFinishUp                     ; Jump if so.
        jmp     FinishUp                        ; Skip receiving this packet.
;
;***************************************************************\
;                                                               *
; Receive error paths.                                          *
;                                                               *
;***************************************************************/
;
HandleRxErrors:

        test    ah, 10h                         ; Buffer Overflow?
        jnz     short HandleBufferOverflowCase  ; Jump if so.
;
;***************************************************************\
;                                                               *
; Handle Receive Errors.                                        *
;                                                               *
;***************************************************************/
;
        mov     al, 4                           ; Reset the RX Error Bit.
        out     dx, al
        mov     edx, [ebp].ReceiveStatus        ; Find out which type
        in      al, dx                          ; caused receive error.
        test    al, FIFOOverrunErrorBit         ; FIFO overrun?
        jz      short NotOverrunError           ; Jump if not.
        inc     [ebp].RxFIFOOverrunErrorCount   ; Inform diagnostics.
        inc     [ebp].PacketRxMiscErrorCount
NotOverrunError:
        xor     eax, eax                        ; Clear accumulator.
        mov     edx, [ebp].TallyCounter0        ; Frame Alignment error counter.
        in      al, dx                          ; Read counter.
        add     [ebp].RxAbortFrameAlignment, eax        ; Add to statistics.
        add     [ebp].PacketRxMiscErrorCount, eax

        inc     edx                             ; CRC error counter.
        in      al, dx                          ; Read counter.
        add     [ebp].ChecksumErrorCount, eax   ; Add to statistics.

        inc     edx                             ; Missed Packet error counter.
        in      al, dx                          ; Read counter.
        add     [ebp].RxMissedPacketCount, eax  ; Add to statistics.
        add     [ebp].PacketRxMiscErrorCount, eax

        jmp     PollAgain                       ; Test for other interrupts.
;
;***************************************************************\
;                                                               *
; Handle Receive Buffer Overflow errors.                        *
;                                                               *
;***************************************************************/
;
HandleBufferOverflowCase:
        inc     [ebp].PacketRxOverflowCount     ; Inform diagnostics.

        mov     edx, [ebp].Command              ; Read Command port.
        in      al, dx
        mov     cl, al                          ; Save Tx Active bit.

        mov     al, NIC_Page0Stop               ; Put NIC in STOP mode.
        out     dx, al

        add     edx, IRemoteByteCount0 - ICommand       ; EDX = RemoteByteCount0.
        xor     eax, eax                        ; Clear RemoteByteCount
        out     dx, al                          ; Clear LSB of Count.
        inc     edx                             ; EDX = RemoteByteCount1.
        out     dx, al                          ; Clear MSB of Count.

                                                ; JCP, 941108 *Begin*
;       call    ReadTickCounter                 ; Get initial tick.
;       mov     ebx, eax                        ; EBX = Initial tick value.
;
;***************************************************************\
;                                                               *
; Idle about 1.6ms to allow current Tx or Rx to finish.         *
;                                                               *
;***************************************************************/
;
;WaitForTxOrRxToFinish:
;       call    ReadTickCounter                 ; Get current tick.
;       neg     eax
;       add     eax, ebx                        ; EAX = Current-Initial Tick.
;       cmp     eax, 4096                       ; 1.7ms passed yet?
;       jb      WaitForTxOrRxToFinish           ; Jump if not

        call    MSMGetMicroTimer                ; EAX = current count.
        mov     ebx, eax                        ; ESI = current tick.
        neg     ebx                             ; Set to negative value.

WaitForTxOrRxToFinish:
        call    MSMGetMicroTimer                ; EAX = current count.
        add     eax, ebx                        ; Time yet ?
        cmp     eax, 1600                       ; 1.6 msec.
        jb      WaitForTxOrRxToFinish           ; Jump if below 1.6 msec.
                                                ; JCP, 941108 *End*
        mov     edx, [ebp].InterruptStatus
        mov     al, 10h                         ; Reset the overflow bit.
        out     dx, al

        test    cl, NIC_TxInProgress            ; Txing during overflow?
        jz      short NoTxActiveAtOverflow      ; Jump if not.

        in      al, dx                          ; Read Interrupt Status.
        test    al, NIC_TxComplete              ; TxComplete or TxError?
        jnz     short NoTxActiveAtOverflow      ; Jump if so.

        inc     [ebp].RetryTxFlag               ; Set Tx cancelled flag so we
                                                ; can restart transmit later.
;
;***************************************************************\
;                                                               *
; Put NIC into LoopBack mode until we empty receive buffers.    *
;                                                               *
;***************************************************************/
;
NoTxActiveAtOverflow:
        mov     edx, [ebp].TransmitConfiguration
        mov     al, 4                           ; Using external loopback fixes
                                                ; overflow hangs on rev C 8390
                                                ; parts on IPXLoad test.
        out     dx, al
;
;***************************************************************\
;                                                               *
; Re-enable card in loopback mode.                              *
;                                                               *
;***************************************************************/
;
        sub     edx, ITransmitConfiguration - ICommand  ; EDX = Command register.
        mov     al, NIC_Page0                   ; Reenable card in loopback
        out     dx, al                          ;  mode.

        inc     [ebp].OverflowRestartFlag       ; Set restart flag.
        mov     ah, [ebp].NextPage              ; AL = Page of next buffer.
        jmp     TestForReceive                  ; Empty packets from ring.
;
;***************************************************************\
;                                                               *
; Received packet bit was set but no packet where in the ring.  *
;                                                               *
;***************************************************************/
;
DidntGetAnything:

        add     edx, IInterruptStatus - ICommand        ; EDX = Int status.
        mov     al, 1                           ; Reset the RX Bit.
        out     dx, al
        inc     [ebp].GotNothingCount           ; Increment Stat counter.
        jmp     PollAgain                       ; Check for more interrupts.
;
;***************************************************************\
;                                                               *
; Check For Transmit Complete or Transmit Error.                *
;                                                               *
;***************************************************************/
;
CheckForTransmit:
        test    ah, NIC_TxComplete              ; Tx finished? (0Ah)
        jz      GetOut                          ; No. Must be finished.

        mov     al, 0Ah                         ; EDX = Interrupt Status Port.
        out     dx, al                          ; Reset the transmit bits.

        test    ah, NIC_TransmitError           ; Was it a bad TX? (08h)
        jnz     Bad_Transmit                    ; Jump if so.

        mov     edx, [ebp].NumberCollisions     ; Find out if transmission
        in      al, dx                          ; experienced any collisions.
        movzx   ecx, al                         ; Clear upper bits.
        cmp     ecx, 1                          ; Single collision?
        je      short CheckTxSingleCol          ; Jump if so.
        add     [ebp].TxOKMultipleCollisions, ecx       ; Add to statistics.
        jmp     short TransmitNextPacket

CheckTxSingleCol:
        inc     [ebp].TxOKSingleCollision       ; Inc single col stat.
TransmitNextPacket:
        inc     [ebp].MSMTxFreeCount            ; Free up transmit buffer.
        mov     [ebp].TxStartTime, 0            ; Zero out Tx Start time.

IF      BackToBackSends
        test    [ebp].TransmitStatusFlag, 0100b ; Another Tx waiting?
        jz      short NothingQueuedOnCard       ; Jump if not.
;
;***************************************************************\
;                                                               *
; We just completed this send, but there is already another     *
; packet transfered to the board. We just need to issue the     *
; send for it.                                                  *
;                                                               *
;***************************************************************/
;
        inc     [ebp].BackToBackSendCount       ; Inform diagnostics.

        mov     al, [ebp].TransmitPage0         ; Assume Page #1.
        mov     ecx, [ebp].TransmitSize1        ; Assume TxSize #1.

        test    [ebp].TransmitStatusFlag, 0010b ; Were we sending to page #2?
        jnz     short IssueTheSend              ; Jump if so.

        mov     al, [ebp].TransmitPage1         ; AL = Page #2.
        mov     ecx, [ebp].TransmitSize2        ; ECX = TxSize #2.

IssueTheSend:
        mov     edx, [ebp].TransmitPage         ; EDX = TxPage Register.
        out     dx, al                          ; Send Tx Page.

        mov     eax, ecx                        ; EAX = TxSize.
        inc     edx                             ; EDX = TxByteCount0 register.
        out     dx, al                          ; Send LSB of TxByteCount.
        inc     edx                             ; EDX = TxByteCount1 register.
        mov     al, ah                          ; AL = MSB of TxByteCount.
        out     dx,al                           ; Send MSB of TxByteCount.

        sub     edx, ITransmitByteCount1 - ICommand     ; EDX = Command port.
        mov     al, NIC_Transmit                        ; Initiate Transmit.
        out     dx, al

        MSMGetCurrentTime                       ; EAX = CurrentTime.
        mov     [ebp].TxStartTime, eax          ; Record current time.

        mov     [ebp].RetryCounter, MaxRetries  ;  Page 1.

        xor     [ebp].TransmitStatusFlag, 0110b ; 5->3, 7->1.
        jmp     short CheckForMoreSends         ; Try to queue another Tx.

NothingQueuedOnCard:
        mov     [ebp].TransmitStatusFlag, 0010b ; No Tx in progress.
                                                ; Buffer #1 is free.
CheckForMoreSends:
ENDIF
;
;***************************************************************\
;                                                               *
; See if there is something else that needs to be transmitted.  *
;                                                               *
;***************************************************************/
;
;JCP    test    [ebp].MSMStatusFlags, TXQUEUED  ; Anything in send queue.
;JCP    jz      PollAgain                       ; Jump to Polling if not.
;JCP    inc     [ebp].EnqueueSendCount          ; Increment statistics.
        call    EtherTSMGetNextSend             ; Send next ECB enqueued.
        jnz     PollAgain                       ; Poll if nothing queued.
        inc     [ebp].EnqueueSendCount          ; Increment statistics. JCP
if UseFastCalls
        push    ebp
        push    offset DriverISRSendReturn
        cmp     [ebp].NICRAMSegmentBase, 0      ; Shared RAM?
        jne     DriverSendSharedRAM             ; Jump if so.
        jmp     DriverSend                      ; Send it.
DriverISRSendReturn:
        pop     ebp
else
        call    DriverSend                      ; Send it.
endif
        jmp     PollAgain                       ; Check for receives.
;
;***************************************************************\
;                                                               *
; Tx error path.                                                *
;                                                               *
;***************************************************************/
;
Bad_Transmit:
        sub     edx, IInterruptStatus - ITransmitStatus ; EDX = Tx Status Reg.
        in      al,dx                                   ; Get TX status.

        test    al, NIC_ExcessCollisions        ; Excess collisions?
        jz      short CheckFIFO                 ; Jump if not.

        inc     [ebp].TxAbortExcessCollisions   ; Increment stat counter.
        inc     [ebp].PacketTxMiscErrorCount
        jmp     short FinishUpTransmit          ; Retry transmit.

CheckFIFO:
        test    al, NIC_FIFO_Underrun           ; FIFO Underrun?
        jz      short CheckCollisions           ; Jump if not.

        inc     [ebp].UnderrunErrorCount        ; Increment stat counter.
        inc     [ebp].PacketTxMiscErrorCount
CheckCollisions:
        test    al, NIC_Collisions              ; Any Collisions?
        jz      short FinishUpTransmit          ; Jump if not.

        mov     edx, [ebp].NumberCollisions     ; Find out how many collisions
        in      al, dx                          ; were detected.
        movzx   eax, al                         ; Clear upper bits.
        cmp     eax, 1                          ; Single collision?
        je      CheckColSingleCol               ; Jump if so.
        add     [ebp].TxOKMultipleCollisions, eax       ; Add to statistics.
        jmp     short FinishUpTransmit
CheckColSingleCol:
        inc     [ebp].TxOKSingleCollision       ; Inc single col stat.
;
;***************************************************************\
;                                                               *
; Attempt to retry this transmission.                           *
;                                                               *
;***************************************************************/
;
FinishUpTransmit:
        dec     [ebp].RetryCounter              ; Any more retries?
        jz      TransmitNextPacket              ; Give up if not.

        inc     [ebp].RetryTxCount              ; Inform diagnostics.
;
;***************************************************************\
;                                                               *
; Program the NIC to re-start the transmit.                     *
;                                                               *
;***************************************************************/
;
IF      BackToBackSends
        mov     al, [ebp].TransmitPage0         ; Assume Page #1.
        mov     ecx, [ebp].TransmitSize1        ; EAX = Tx size for page #1.
        test    [ebp].TransmitStatusFlag, 0010b ; Need to send to page #1?
        jz      short ReTxSizeSet               ; Jump if so.

        mov     al, [ebp].TransmitPage1         ; Set to Page #2.
        mov     ecx, [ebp].TransmitSize2        ; EAX = Tx size for page #2.
ReTxSizeSet:

        out     dx, al                          ; Set TxPage register.
        mov     eax, ecx                        ; EAX = Transmit Size.
        inc     edx                             ; EDX = TxByteCount0 register.
ELSE
        mov     edx, [ebp].TransmitByteCount0
        mov     eax, [ebp].TransmitSize1
ENDIF
        out     dx, al                          ; Send LSB of TxByteCount.
        inc     edx                             ; EDX = TxByteCount1 register.
        mov     al, ah                          ; AL = MSB of TxByteCount.
        out     dx, al                          ; Send MSB of TxByteCount.

        sub     edx, ITransmitByteCount1 - ICommand
        mov     al, NIC_Transmit                ; Issue Tx command.
        out     dx, al

        MSMGetCurrentTime                       ; EAX = Current Time.

        mov     [ebp].TxStartTime, eax          ; Record Tx Start.
        jmp     PollAgain                       ; Check status again.
;
;***************************************************************\
;                                                               *
; Time to exit ISR.                                             *
;                                                               *
;***************************************************************/
;
GetOut:

IF NOT  UseFastCalls
        dec     [ebp].InDriverISR               ; Not in ISR.
ENDIF
IF      UseFastCalls
        ret
ELSE
        MSMServiceEventsAndRet                  ; Let LSL unqueue returned
        ;ret                                    ;  ECB's and return.
ENDIF

ClearIntsAndGetOut:
        mov     edx, [ebp].InterruptStatus      ; Read the NIC ISR.
        in      al,dx                           ; AL and
        out     dx, al
IF NOT  UseFastCalls
        dec     [ebp].InDriverISR               ; Not in ISR.
ENDIF
IF      UseFastCalls
        ret
ELSE
        MSMServiceEventsAndRet                  ; Let LSL unqueue returned
        ;ret                                    ;  ECB's and return.
ENDIF

DriverISR       endp
        subttl  -- DriverDisableInterrupt --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( DriverDisableInterrupt, NE2000/API/DISINT )
;
; Name:         DriverDisableInterrupt
;
; Description:  This routine will disable the adapters ability to
;               interrupt the host.
;
; On Entry:     EAX     N/A
;               EBX     N/A
;               ECX     N/A
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     N/A
;               EDI     N/A
;
;               Note:   Interrupts are disabled.
;
; On Return:    EAX     Destroyed
;               EBX     Preserved
;               ECX     Preserved
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Preserved
;               EDI     Preserved
;
;               Flags:
;
;               Note:   Interrupts disabled.
;
; Remarks:      This routine is called by the MSM.
;
; See Also:     DriverEnableInterrupt
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
        align   16
DriverDisableInterrupt  proc
if DEBUG
        push    eax
        mov     al, 'D'
        call    OutChar
        pop     eax
endif
        mov     edx, [ebp].InterruptMask
        mov     al, NIC_MaskByte
        out     dx, al
        xor     eax, eax
        ret

DriverDisableInterrupt  endp
        subttl  -- DriverEnableInterrupt --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( DriverEnableInterrupt, NE2000/API/ENINT )
;
; Name:         DriverEnableInterrupt
;
; Description:  This routine will enable the adapters ability to
;               interrupt the host.
;
; On Entry:     EAX     N/A
;               EBX     N/A
;               ECX     N/A
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     N/A
;               EDI     N/A
;
;               Note:   Interrupts are disabled.
;
; On Return:    EAX     Destroyed
;               EBX     Preserved
;               ECX     Preserved
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Preserved
;               EDI     Preserved
;
;               Flags:
;
;               Note:   Interrupts disabled.
;
; Remarks:      This routine is called by the MSM.
;
; See Also:     DriverDisableInterrupt
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
        align   16
DriverEnableInterrupt   proc

if DEBUG
        push    eax
        mov     al, 'E'
        call    OutChar
        pop     eax
endif

        mov     edx, [ebp].InterruptMask
        mov     al, NIC_UnmaskByte
        out     dx, al
        ret

DriverEnableInterrupt   endp

;       Removed by JCP. (941108)
;
;       subttl  -- ReadTickCounter --
;       page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( ReadTickCounter, NE2000/INTERNAL/TICK )
;
; Name:         ReadTickCounter
;
; Description:  This routine reads the timer tick count register which
;               decrements by 2 every 65536 to 0 every 1/36.414 times a
;               second. Each 2 decrements of the count the count represents
;               838 nsec's.
;
; On Entry:     EAX     N/A
;               EBX     N/A
;               ECX     N/A
;               EDX     N/A
;               EBP     N/A
;               ESI     N/A
;               EDI     N/A
;
;               Note:   Interrupts in any state.
;
; On Return:    EAX     Current Count
;               EBX     Preserved
;               ECX     Preserved
;               EDX     Preserved
;               EBP     Preserved
;               ESI     Preserved
;               EDI     Preserved
;
;               Flags:
;
;               Note:   Interrupts preserved.
;
; Remarks:      This routine is called by DriverISR.
;               It is called at interrupt time.
;
; See Also:     DriverISR.
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
;ReadTickCounter proc    near
;
;       xor     eax, eax                ; Clear accumulator.
;       mov     al, 06h                 ; Command 8254 to latch T0's count.
;       out     43h, al
;
; Now you can read the count.
;
;       in      al, 40h                 ; Read LSB of latched count.
;       mov     ah, al                  ; Save in AH.
;       in      al, 40h                 ; Read MSB of latched count.
;       xchg    al, ah                  ; Put into proper order.
;       ret
;
;ReadTickCounter endp

        public  DriverReset
        subttl  -- DriverReset --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( DriverReset, NE2000/API/RESET )
;
; Name:         DriverReset
;
; Description:  This routine will reset and initialize the NIC.
;
; On Entry:     EAX     set to OP_SCOPE_ADAPTER if the adapter specified by EBP
;         is to be reset. Otherwise set to OP_SCOPE_LOGICAL_BOARD
;         which indicates that only the logical board specified
;                       by EBX is to be reset.
;               EBX     @ Frame Data Space
;               ECX     N/A
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     N/A
;               EDI     N/A
;
;               Note:   Interrupts are disabled.
;
; On Return:    EAX     0 if successful(otherwise points to error message)
;               EBX     Preserved
;               ECX     Destroyed
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Destroyed
;               EDI     Destroyed
;
;               Flags:
;
;               Note:   Interrupts disabled.
;
; Remarks:      This routine is called by the MSM media module.
;               It is called at process time.
;
; See Also:     ETHERTSM\EtherTSMReset
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
DriverReset     proc    near

   cmp   eax, OP_SCOPE_ADAPTER
        je   ResetAdapter

   xor   eax, eax
        ret

ResetAdapter:              
        inc     [ebp].AdapterResetCount         ; Increment stat counter.

        cmp     [ebp].NICRAMSegmentBase, 0      ; Shared RAM?
        je      DriverResetIO                   ; Jump if not.

        mov     edx, [ebp].Control2             ; DX = Control 2.
        mov     al, [ebp].Control2Value
;;      mov     eax, 0c1h                       ; Set to 16 bit mode.
        out     dx, al

        mov     edx, [ebp].Control1             ; DX = Control 1.
        mov     al, [ebp].Control1Value
        or      al, 10000000b                   ; Set Reset line.
        out     dx, al                          ; Reset adapter.
        SLOW
        and     al, 01111111b                   ; Release Reset line.
        out     dx, al
        jmp     short DriverResetStop

DriverResetIO:
        mov     edx, [ebp].Reset                ; Initialize the NIC.
        in      al,dx                           ; Reading this port sets
                                                ;  a hard reset.
        SLOW
        out     dx,al                           ; Release reset.

DriverResetStop:
        mov     edx, [ebp].Command              ; EDX = Command register.
        mov     al, NIC_Page0Stop               ; Set to page 0 registers.
        out     dx,al

        cmp     [ebp].FirstTimeInit, 1          ; In driver init?
        je      CheckPort                       ; Yes. Check command port.

CheckPortReturn:
        mov     edx,[ebp].DataConfiguration     ; EDX = DataConfiguration register.
        mov     al,49h                          ; Set to word mode.
        out     dx,al

        mov     edx, [ebp].RemoteByteCount0     ; Clear RBCR0 and RBCR1.
        xor     al, al
        out     dx, al                          ; Clear LSB of RemoteByteCount.
        inc     edx                             ; EDX = RemoteByteCount1.
        out     dx, al                          ; Clear MSB of RemoteByteCount.

        mov     [ebp].OverflowRestartFlag, al   ; Clear in case Paging error
        mov     [ebp].RetryTxFlag, al           ; occured after overflow.

        call    EtherTSMUpdateMulticast         ; Init Multicast registers.
;
;***************************************************************\
;                                                               *
; We need to be in loopback mode to prevent getting a receive   *
; during the RAM tests.                                         *
;                                                               *
;***************************************************************/
;
        mov     ecx, PSTART                     ; CL = I/O PSTART.
        mov     edi, PSTOP                      ; EDI = I/O PSTOP.
        cmp     [ebp].NICRAMSegmentBase, 0      ; Shared RAM?
        je      short DriverResetPSTART         ; Jump if not.
        mov     ecx, RAMPSTART                  ; CL = Shared RAM PSTART.
        mov     edi, RAMPSTOP                   ; EDI = Shared RAM PSTOP.
DriverResetPSTART:

        mov     edx, [ebp].TransmitConfiguration        ; EDX = TxConfiguration register.
        mov     al, 2                                   ; Put in loopback mode.
        out     dx, al

        mov     edx, [ebp].PageStart            ; Set RX start to beginning of RAM.
        mov     al, cl
        out     dx, al

        mov     edx, [ebp].Boundary             ; Set Boundary to PSTART.
        out     dx, al

        mov     edx, [ebp].PageStop             ; Set RX stop to end of RAM.
        mov     eax, edi
        out     dx, al

        mov     edx, [ebp].InterruptStatus      ; Clear all interrupt bits.
        mov     al, 0FFh
        out     dx, al


if DEBUG
        push    eax
        mov     al, 'e'
        call    OutChar
        pop     eax
endif

        mov     edx, [ebp].InterruptMask        ; EDX = InterruptMask register.
        mov     al, NIC_UnmaskByte              ; Unmask all interrupts.
        out     dx, al

        mov     edx, [ebp].Command              ; Switch to page 1 registers.
        mov     al, NIC_Page1Stop
        out     dx, al

        mov     edx, [ebp].Current              ; Set current page to
        lea     eax, [ecx + 1]                  ; Beginning of ring + 1.
        out     dx, al

        mov     [ebp].NextPage, al              ; Initialize NextPage.

        mov     edx, [ebp].Command
        mov     al, NIC_Page0Stop               ; Switch back to page 0
        out     dx, al                          ; registers.
;
;***************************************************************\
;                                                               *
; If this is the first time through, then do some hardware      *
; tests and initialize the physical address register.           *
;                                                               *
;***************************************************************/
;
        cmp     [ebp].FirstTimeInit,1           ; First time thru?
        jne     short SkipHardwareTests         ; Skip if not.

        call    DriverTestHardware              ; Test the hardware.
        or      eax, eax                        ; Error?
        jnz     short DeadHardware              ; Jump if so.

SkipHardwareTests:
IF      BackToBackSends
        mov     edx, [ebp].TransmitPage         ; This fix for collisions.
        mov     al, [ebp].TransmitPage0         ; AL = page C0h.

InitIssueTheSend:
        out     dx, al                          ; Set proper Transmit page.

   mov   [ebp].TransmitStatusFlag, 0010b   ; Set to Initial value
   mov     [ebp].MSMTxFreeCount, 2         ; Allow 2 transmits simultaneously.

ELSE
        mov     edx, [ebp].TransmitPage
        mov     al, [ebp].TransmitPage0
        out     dx, al

   mov     [ebp].MSMTxFreeCount, 1         ; Allow 1 transmit.

ENDIF

        mov     edx, [ebp].Command              ; EDX = Command register.
        mov     al, NIC_Page0                   ; Start the NIC.
        out     dx, al
;
;***************************************************************\
;                                                               *
; Take NIC out of Loopback mode.                                *
;                                                               *
;***************************************************************/
;
        mov     edx, [ebp].TransmitConfiguration
        xor     eax, eax                        ; Mode 0 transmit.
        out     dx, al
        mov     [ebp].OverflowRestartFlag, al   ; Reset Overflow flag.

        ret                                     ; Init successful.
;
;***************************************************************\
;                                                               *
; Check for valid command port response.                        *
;                                                               *
;***************************************************************/
;
CheckPort:
        in      al, dx                          ; Read command register.
        cmp     al, 21h                         ; Is the i/o port alive ?
        je      CheckPortReturn                 ; Yes, proceed.

        cmp     al, 23h                         ; Try again.
        je      CheckPortReturn                 ; Yes, proceed.

        lea     eax, PortFailMessage            ; EAX -> Error Message.

DeadHardware:
        push    eax                             ; Save error message.
        mov     edx, [ebp].Command              ; EDX = Command register.
        mov     al, NIC_Page0Stop               ; Stop the NIC.
        out     dx, al

        pop     eax                             ; Restore error message.
        or      eax, eax
        ret

DriverReset     endp
        subttl  -- DriverTestHardware --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( DriverTestHardware, NE2000/INTERNAL/TEST )
;
; Name:         DriverTestHardware
;
; Description:  This routine will use either the configuration tables or
;               NIC's node address to set the NIC's physical registers and
;               test the NIC's static RAM.
;
; On Entry:     EAX     N/A
;               EBX     @ Frame Data Space
;               ECX     N/A
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     N/A
;               EDI     N/A
;
;               Note:   Interrupts are disabled.
;
; On Return:    EAX     0 if successful(otherwise points to error message)
;               EBX     Preserved
;               ECX     Destroyed
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Destroyed
;               EDI     Destroyed
;
;               Flags:
;
;               Note:   Interrupts disabled.
;
; Remarks:      This routine is called by the DriverReset.
;               It is called at process time.
;
; See Also:     DriverReset
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
DriverTestHardware      proc
;
;***************************************************************\
;                                                               *
; Now, read my physical address out of the ROM and program the  *
; chip as well as saving it for later.                          *
;                                                               *
;***************************************************************/
;
        cmp     [ebp].NICRAMSegmentBase, 0      ; Shared RAM?
        jne     DriverTestSharedRAM             ; Jump if so.

        mov     edx, [ebp].RemoteStartAddress0  ; EDX = RemoteStartAddress0.
        xor     al, al                          ; Start at beginning of ROM.
        out     dx, al                          ; Send LSB of RemoteStartAddress.
        inc     edx                             ; EDX = RemoteStartAddress1.
        out     dx, al                          ; Send MSB of RemoteStartAddress.

        inc     edx                             ; EDX = RemoteByteCount0.
        mov     ax, 16 * 2                      ; Read 16 bytes.
        out     dx, al                          ; Send LSB of RemoteByteCount.
        inc     edx                             ; EDX = RemoteByteCount1.
        mov     al, ah                          ; AL = MSB of RemoteByteCount.
        out     dx, al                          ; Send MSB of RemoteByteCount.

        mov     edx, [ebp].Command              ; EDX = Command register.
        mov     al, NIC_RemoteDmaRd             ; Issue Remote DMA read
        out     dx, al                          ; to card.

        mov     edx, [ebp].NICData              ; EDX = NIC Data port.

        lea     edi, [ebx].MLIDNodeAddress      ; EDI -> Node Address.
        mov     esi, edi                        ; ESI -> Node Address.
;
;***************************************************************\
;                                                               *
; First make sure node wasn't forced from command line.         *
;                                                               *
;***************************************************************/
;
        cmp     dword ptr [ebx].MLIDNodeAddress, -1
        jz      short GetNodeFromProm
        lea     edi, [ebp].ReceiveHeader        ; Copy PROM's node to dummy.
GetNodeFromProm:
;
;***************************************************************\
;                                                               *
; Read Node address from card.                                  *
;                                                               *
;***************************************************************/
;
        rept    6
        in      al, dx                          ; Read PROM's Node byte.
        stosb                                   ; Store it.
        endm
;
;***************************************************************\
;                                                               *
; Make sure this is really an NE2000 by searching for 'WW' in   *
; next ten bytes of the PROM.                                   *
;                                                               *
;***************************************************************/
;
        mov     ecx, 10                         ; Check next ten bytes.
CheckSignatureLoop:
        in      al, dx                          ; Read next byte from PROM.
        cmp     ecx, 4                          ; Checksum?
        je      short SkipChecksum              ; Jump if so.
        cmp     ecx, 3                          ; Checksum?
        je      short SkipChecksum              ; Jump if so.
        cmp     al, 'W'                         ; NE2000 in 16-bit slot?
        jz      short NICMightBeIn16BitSlot     ; Jump if so.
        cmp     al, 'B'                         ; NE2000 in 8-bit slot?
        jz      short NICMightBeIn8BitSlot      ; Jump if so.
SkipChecksum:
        dec     ecx
        jnz     short CheckSignatureLoop        ; Check next PROM byte.
        jmp     NICNotInSlot                    ; NE2000 not there.

NICMightBeIn16BitSlot:
        dec     ecx                             ; Any more bytes to read?
        jz      NICNotInSlot                    ; Exit if not.
        in      al, dx                          ; Read next byte from PROM.
        cmp     al, 'W'                         ; NE2000 in 16-bit slot?
        jz      short NICIn16BitSlot            ; Jump if so.
        dec     ecx
        jnz     short CheckSignatureLoop        ; Check next PROM byte.
        jmp     NICNotInSlot                    ; NE2000 not there.
NICMightBeIn8BitSlot:
        dec     ecx                             ; Any more bytes to read?
        jz      NICIsNE1000                     ; Exit if not.
        in      al, dx                          ; Read next byte from PROM.
        cmp     al, 'B'                         ; NE2000 in 8-bit slot?
        jz      NICIn8BitSlot                   ; Jump if so.
        jmp     NICIsNE1000                     ; Trying to access an NE1000.

NICIn16BitSlot:

        mov     ecx, 6

        mov     edx, [ebp].Command              ; EDX = Command register.
        mov     al, NIC_Page1Stop               ; Stop the NIC and enable
        out     dx, al                          ; page 1 registers.

        mov     edx, [ebp].PhysicalReg0         ; EDX = Physical register 0.

AddressToChipLoop:
        lodsb                                   ; Get next byte.
        out     dx, al                          ; Send it to card.

        rept    10
        nop
        endm

        inc     edx                             ; Go to next Physical reg.
        dec     ecx
        jnz     AddressToChipLoop               ; Get next node byte.

        mov     edx, [ebp].Command              ; EDX = Command register.
        mov     al, NIC_Page0Stop               ; Set to Page 0.
        out     dx, al
;
;***************************************************************\
;                                                               *
; Now that we know we are really dealing with an NE2000 in the  *
; right slot, we can safely test memory.              *
; NE2000 contains Static RAM. Running a checkerboard test will  *
; catch retention, open or shorted faults common to S-RAM.      *
;                                                               *
;***************************************************************/
;
        push    ebx                             ; Save Board Base.
        mov     bx, 0a5a5h                      ; Perform basic checkerboard
        call    DoRAMTest                       ; memory test on card.
        pop     ebx                             ; Restore Board Base.
        jnz     short MemoryFailure             ; Jump if memory test failed.

        push    ebx                             ; Save Board Base.
        mov     bx, 05a5ah                      ; Complement pattern and
        call    DoRAMTest                       ; test again.
        pop     ebx                             ; Restore Board Base.
        jnz     short MemoryFailure             ; Jump if memory test failed.
;
;***************************************************************\
;
; Now verify that the IRQ which was selected matches that of    *
; the hardware.
;                                                               *
;***************************************************************/
;
        xor     eax, eax                        ; Init successful.
        ret

DriverTestSharedRAM:
        lea     edi, [ebx].MLIDNodeAddress
        mov     esi, edi

        cmp     dword ptr [ebx].MLIDNodeAddress, -1
        jne     NICIn16BitSlot

        mov     edx, [ebp].Prom                 ; DX = Prom I/O port.
        mov     ecx, 6
DriverTestSharedLoop:
        in      al, dx
        inc     edx
        stosb
        loop    DriverTestSharedLoop
        jmp     NICIn16BitSlot
;
;***************************************************************\
;                                                               *
; DriverTestHardware error paths.                               *
;                                                               *
;***************************************************************/
;
NICIn8BitSlot:
        lea     eax, NICIn8BitSlotMessage
        ret

NICNotInSlot:
        lea     eax, NICNotInSlotMessage
        ret

NICIsNE1000:
        lea     eax, NICIsNE1000Message
        ret

MemoryFailure:
        lea     eax, BufferMemoryFailMessage
        ret


DriverTestHardware      endp
        subttl  -- DoRAMTest --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( DoRAMTest, NE2000/INTERNAL/RAMTEST )
;
; Name:         DoRAMTest
;
; Description:  This routine will test Static RAM by writing the pattern
;               provided to all areas of memory and reading them back.
;
; On Entry:     EAX     N/A
;               EBX     Test Pattern
;               ECX     N/A
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     N/A
;               EDI     N/A
;
;               Note:   Interrupts are disabled.
;
; On Return:    EAX     Destroyed
;               EBX     Preserved
;               ECX     Destroyed
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Preserved
;               EDI     Preserved
;
;               Flags:
;
;               Note:   Interrupts disabled.
;
; Remarks:      This routine is called by the DriverTestHardware.
;               It is called at process time.
;
; See Also:     DriverTestHardware
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
DoRAMTest       proc    near            ; NE2000 RAM Test.
        cmp     [ebp].NICRAMSegmentBase, 0      ; Shared RAM?
        jne     DoSharedRAMTest                 ; Jump if so.
;
;***************************************************************\
;                                                               *
; Set Remote Start Address = 4000h.                             *
;                                                               *
;***************************************************************/
;
        mov     edx, [ebp].RemoteStartAddress0  ; EDX = RemoteStartAddress0
        mov     al, 0                           ; AL = LSB of RemoteStartAddress.
        out     dx, al                          ; Send LSB of RemoteStartAddress.
        inc     edx                             ; EDX = RemoteStartAddress1.
        mov     al, 40h                         ; AL = MSB of RemoteStartAddress.
        out     dx, al                          ; Sedn MSB of RemoteStartAddress.
;
;***************************************************************\
;                                                               *
; Set Remote Byte Count = 4000h.                                *
;                                                               *
;***************************************************************/
;
        inc     edx                             ; EDX = RemoteByteCount0.
        mov     al, 0                           ; AL = LSB of RemoteByteCount.
        out     dx, al                          ; Send LSB of RemoteByteCount.
        inc     edx                             ; EDX = RemoteByteCount1.
        mov     al, 40h                         ; AL = MSB of RemoteByteCount.
        out     dx, al                          ; Send MSB of RemoteByteCount.
;
;***************************************************************\
;                                                               *
; Issue a Remote DMA Write to NIC.                              *
;                                                               *
;***************************************************************/
;
        mov     edx, [ebp].Command      ; EDX = Command port.
        mov     al, NIC_RemoteDmaWr
        out     dx, al
        mov     edx, [ebp].NICData      ; EDX = Data port.

        mov     ecx, 2000h              ; Write 2000h bytes.
        mov     ax, bx                  ; Write test value.

TestOutLoop:
        out     dx, ax                  ; Send test value.
   SLOW            ; 
        dec     ecx
        jnz     TestOutLoop             ; Send next one.
;
;***************************************************************\
;                                                               *
; Now re-read it back in and check for accuracy.                *
;                                                               *
; Set Remote Start Address = 4000h.                             *
;                                                               *
;***************************************************************/
;
        mov     edx, [ebp].RemoteStartAddress0  ; EDX = RemoteStartAddress0
        mov     al, 0                           ; AL = LSB of RemoteStartAddress.
        out     dx, al                          ; Send LSB of RemoteStartAddress.
        inc     edx                             ; EDX = RemoteStartAddress1.
        mov     al, 40h                         ; AL = MSB of RemoteStartAddress.
        out     dx, al                          ; Sedn MSB of RemoteStartAddress.
;
;***************************************************************\
;                                                               *
; Set Remote Byte Count = 4000h.                                *
;                                                               *
;***************************************************************/
;
        inc     edx                             ; EDX = RemoteByteCount0.
        mov     al, 0                           ; AL = LSB of RemoteByteCount.
        out     dx, al                          ; Send LSB of RemoteByteCount.
        inc     edx                             ; EDX = RemoteByteCount1.
        mov     al, 40h                         ; AL = MSB of RemoteByteCount.
        out     dx, al                          ; Send MSB of RemoteByteCount.
;
;***************************************************************\
;                                                               *
; Issue Remote DMA read to NIC.                                 *
;                                                               *
;***************************************************************/
;
        mov     edx, [ebp].Command      ; EDX = Command port.
        mov     al, NIC_RemoteDmaRd
        out     dx, al
        mov     edx, [ebp].NICData      ; EDX = Data port.

        mov     ecx, 2000h              ; ECX = Bytes to read.

TestInLoop:
        in      ax, dx                  ; Read byte from card.
   SLOW            ; 
        cmp     bx, ax                  ; Same as test value.
        loopz   TestInLoop              ; Continue if so.

        ret

DoSharedRAMTest:
        cld
        mov     edi, [ebp].NICRAMSegmentBase    ; EDI -> Cards RAM.
        mov     ecx, 4000h/4                    ; ECX = count.
        mov     eax, ebx                        ; EAX = Test value.
        rep     stosd                           ; Write it to RAM.

        mov     edi, [ebp].NICRAMSegmentBase    ; EDI -> Cards RAM.
        mov     ecx, 4000h/4                    ; ECX = count.(EAX = test val)
        repe    scasd                           ; Scan memory using test value.

        ret

DoRAMTest       endp
        subttl  -- VerifyIRQ --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( VerifyIRQ, NE2000/INTERNAL/VERIFY )
;
; Name:         VerifyIRQ
;
; Description:  This routine will verify that the IRQ level selected by the
;      user matches the IRQ that the hardware is configured for.
;      We do this by configuring the card for internal loopback
;      then we do transmit to force an interrupt event. If our
;      ISR gets called then everything's fine, otherwise we flag
;      an error condition.
;
; On Entry:     EAX     N/A
;               EBX     @ Frame Data Space
;               ECX     N/A
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     N/A
;               EDI     N/A
;
;               Note:   Interrupts are disabled.
;
; On Return:    EAX     Destroyed
;               EBX     Preserved
;               ECX     Destroyed
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Destroyed
;               EDI     Destroyed
;
;               Flags:   
;
;               Note:   Interrupts disabled.
;
; Remarks:      This routine is called by the DriverTestHardware.
;               It is called at process time.
;
; See Also:     DriverTestHardware
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
VerifyIRQ       proc    near

;AYD Verify IRQ only if it is Shared RAM card SPD#132446

        cmp     [ebp].NICRAMSegmentBase, 0      ; Shared RAM?
        je        VerifyIRQReturn                 ; No.
;
;***************************************************************\
;
; For some reason the loopback test won't work for come cases  *
; for NE2000+ (AT/LANIC chip). However we can still verify the *
; IRQ for NE2000+ by reading the Mode Configuration Register A.*
;                                                              *
;***************************************************************/
;
   xor   eax, eax         ; See if AT/LANIC chip, if so
   mov   edx, [ebp].RemoteByteCount0   ;  a read from RBCR0 will give
   in   al, dx            ;  us Mode Config Register A.

   mov   cl, al            ; Save original value 
   and   al, NOT 18h         ; Write out new value.
   or   al, 20h            
   mov   ah, al            ; Save new value.
   out   dx, al
   SLOW
   in   al, dx            ; Try to read it back.
   cmp   ah, al            ; Did it stick?
   movzx   eax, cl            ; Restore old value
   out   dx, al   
   je        VerifyIRQAtlanic                ; It stuck, must be AT/LANIC
                    ; Else, 8390.
;
;***************************************************************\
;                                                               *
; We need to adjust the Data Configuration, Transmit Config    *
; and Receive Configuration registers for loopback testing.   *
;                                                               *
;***************************************************************/
;

   mov   edx, [ebp].DataConfiguration   ; EDX = DataConfiguration register.
   mov   al, 41h            ; Set to loopback mode.
   out   dx, al

   mov   edx, [ebp].ReceiveConfiguration   ; EDX = ReceiveConfiguration register.
   mov   al, 1Fh            ; Set to Prom mode temporarily.
   out   dx, al

   mov   edx, [ebp].InterruptStatus   ; EDX = Interrupt Status Register
   mov   al, 0ffh         ; Make sure all ints cleared
   out   dx, al

   mov     edx, [ebp].TransmitConfiguration   ; EDX = Transmit Configuration register
   mov     al, 02h            ; Put in loopback for a moment
   out     dx, al

if BackToBackSends            
   mov   [ebp].MSMTxFreeCount, 2      
else
   mov   [ebp].MSMTxFreeCount, 1
endif

;
;***************************************************************\
;                        *
; Now we just need to set the Transmit Page and Byte Counts and   *
; initiate the transmit. Since we don't really care about the   *
; transmit data we won't bother copying any particular data to   *
; the NIC.                     *
;                        *
;***************************************************************/
;
   mov   InDriverInit, 0         ;SPD 125034

   mov   edx, [ebp].TransmitPage      ; EDX = Transmit Page Start.
        mov     al, [ebp].TransmitPage0         ; Set to Tx Page 0
        out     dx, al

   inc   edx            ; EDX = TxByteCount0.
   mov   eax, 40h         ; EAX = Tx size (use 64 bytes).
   out   dx, al            ; Send LSB of Tx Byte Count.

        inc     edx                         ; EDX = TxByteCount1.
        mov     al, ah                     ; AL = MSB of Tx Byte Count.
        out     dx, al                     ; Send MSB of Tx Byte Count.

        sub     edx, ITransmitByteCount1 - ICommand     ; EDX = Command.
        mov     al, NIC_Transmit                        ; Send packet.
        out     dx, al
;
;***************************************************************\
;                                                               *
; Idle about 16ms to allow loopback Tx to finish. If interrupt  *
; occurs then TxFreeCount will have incremented         *
;                                                               *
;***************************************************************/
;
        call    MSMGetMicroTimer                ; EAX = current count.
        mov     edx, eax                        ; ESI = current tick.
        neg     edx                             ; Set to negative value.

WaitForTxToFinish:
   call   MSMYieldWithDelay      ; This will force an sti.
        call    MSMGetMicroTimer                ; EAX = current count.
        add     eax, edx                        ; Time yet ?
        cmp     eax, 16000                      ; 16 msec.
        jb      WaitForTxToFinish              ; Jump if below 16 msec.

   mov   InDriverInit, 1         ;SPD 125034

if BackToBackSends
   cmp   [ebp].MSMTxFreeCount, 2      ; Did TxFreeCount go up?
else
   cmp   [ebp].MSMTxFreeCount, 1      ; Did TxFreeCount go up?   
endif
   jbe   VerifyIRQError         ; No, flag error.

;                  
;***************************************************************\
;                                                               *
; The IRQ is correctly configured. Make sure we put NIC back in *
; the state that Driver Reset expects.
;                                                               *
;***************************************************************/
;
   mov   edx, [ebp].InterruptStatus   ; EDX = Interrupt Status Register
   mov   al, 0ffh         ; Make sure all ints cleared
   out   dx, al

   mov   edx, [ebp].Command      ; EDX = Command Register
   mov   al, NIC_Page0Stop      ; set to Page 0.
   out   dx, al

   mov   edx, [ebp].DataConfiguration   ; EDX = DataConfiguration register.
   mov   al, 49h            ; Set to normal mode.
   out   dx, al

   mov     edx, [ebp].TransmitConfiguration   ; EDX = Transmit Configuration register
   xor     al, al            ; Set to normal mode.
   out     dx, al

   call   EtherTSMUpdateMulticast      ; This will set the Receive
                     ; Configuration register to
                  ; default values.
VerifyIRQReturn:
        xor     eax, eax                        ; Return Success.
        ret

VerifyIRQError:
   or   eax, 1            ; Clear Z flag
   ret

VerifyIRQAtlanic:
   and   al, IntMask         ; Mask off INT bits
   shr   al, 3            ; Make it an index
   mov   ah, SharedRAMIntTable[eax]   ; Convert to IRQ value
   cmp   ah, byte ptr [ebx].MLIDInterrupt   ; Do they match ?
   jne   VerifyIRQError         ; No. Error.
   jmp   VerifyIRQReturn

VerifyIRQ       endp

        subttl  -- DriverInit --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( DriverInit, NE2000/API/INIT )
;
; Name:         DriverInit
;
; Description:  This routine will call EtherTSMRegisterHSM,
;               MSMParseDriverParameters, MSMRegisterHardwareOptions,
;               MSMSetHardwareInterrupt, MSMRegisterMLID, initialize
;               variables in the Adapter Data Space and reset/initialize
;               the card.
;
; On Entry:     EAX     N/A
;               EBX     N/A
;               ECX     N/A
;               EDX     N/A
;               EBP     N/A
;               ESI     N/A
;               EDI     N/A
;
;               Note:   Interrupts are enabled.
;
; On Return:    EAX     0 if successful(otherwise it points to error message)
;               EBX     Preserved
;               ECX     Destroyed
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Preserved
;               EDI     Preserved
;
;               Flags:
;
;               Note:   Interrupts preserved.
;
; Remarks:      This routine is called by the OS at load time.
;               It is called at process time.
;
; See Also:     MSM\MSMParseDriverParameters
;               MSM\MSMRegisterHardwareOptions
;               MSM\MSMSetHardwareInterrupts
;               MSM\MSMRegisterMLID
;               MSM\MSMScheduleIntTimeCallBack
;               MSM\MSMScheduleAESCallBack
;               MSM\MSMEnablePolling
;               DriverReset
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
DriverInit      proc

        CPush
   mov   InDriverInit, 1         ;SPD 125034 BEGIN
   call    MSMGetMicroTimer                       
   mov   edx, eax         ; EDX = Current Time.

WaitForDriverISRLoop:
   call    MSMGetMicroTimer
   sub   eax, edx
   cmp   eax, 50000
   jb   WaitForDriverISRLoop      ;SPD 125034 END

;
;***************************************************************\
;                                                               *
; Fill in Driver Parameter Block fields.                        *
;                                                               *
;***************************************************************/
;
        mov     DriverStackPointer, esp                 ; Fill in stack ->.

        and     NE2000TextLen, NOT T_REQUIRED           ; 'MEM=' not required.
        lea     esi, DriverParameterBlock               ; ESI -> Parm block.
        call    MSMParseCustomKeywords                  ; Parse for 'MEM='.

        lea     esi, DriverParameterBlock               ; ESI -> Parm block.
        call    EtherTSMRegisterHSM                     ; Get EBX.
        jnz     DriverInitError                         ; Jump if error.

;
;  09/13/95 MPK Spec 3.3 changes
;
   push    ebx                              ; Save EBX we will need it
        mov     esi,-1                           ; Set Parameter for call
S33_Bus_Scan:
        call    MSMScanBusInfo                   ; Find out about Bus
        cmp     eax,ODI_NBI_SUCCESSFUL           ; are we done?
   jne     S33_Bus_Scan_Done                ; Yes, exit
        cmp     ecx,ODI_BUSTYPE_ISA              ; is this ISA, (NE2000 ISA only)
   jne     S33_Bus_Scan                     ; no, then keep looking
        pop     ebx                              ; Yes, then place tag in param table

;JCJ 03-July-97 SPD#160730
        mov     [ebx].MLIDBusTag, 00             ; All legacy ISA drivers have BusTag0
;JCJ 03-July-97

        push    ebx                              ; Keep BX straight
        jmp     S33_Bus_Scan                     ; Keep cycling (for completeness)
S33_Bus_Scan_Done:
        pop     ebx                              ; Now we can restore EBX for good
;
;  09/13/95 MPK Spec 3.3 changes End

;
;***************************************************************\
;                                                               *
; EBX -> Frame Data Space(Config Table).                        *
; Let MSM Parse the command line.                               *
;                                                               *
;***************************************************************/
;
        mov     eax, NeedsIOPort0Bit OR NeedsInterrupt0Bit OR CAN_SET_NODE_ADDRESS
        cmp     MemOnCommandLine, 0                     ; 'MEM=' entered?
        je      short ParseParms                        ; Jump if not.
        or      eax, NeedsMemoryDecode0Bit              ; Set so that OS
        mov     ecx, InitMem
        mov     [ebx].MLIDMemoryDecode0, ecx            ; Set up config so
        mov     [ebx].MLIDLength0, 400h                 ;  OS doesn't complain.

ParseParms:
        lea     ecx, AdapterOptions
        call    MSMParseDriverParameters
        jnz     DriverInitError                    ; Jump if error.

;SPD 124386 Remove this next check, we handle all this in CheckForSharedRAM
;now.
;
; Before we go any farther let's check to see if something (anything)
; responds at our PORT address. This prevents us from thinking that there
; is a NE2000+ in shared mem mode present when there is actually no card
; present at all.
;
; movzx   edx, [ebx].MLIDIOPortsAndLengths        ; DX = Base I/O
;   in   al, dx               ; Get current value.
;   mov   ah, al               ; Save in AH.
;   xor   al, 1               ; Write out new value.
;   mov   cl, al               ; Save new value
;   out   dx, al
;   SLOW
;   in   al, dx               ; Try to read it back.
;   cmp   ah, al               ; Anything there?
;   mov   al, ah               ; Restore old value
;   out   dx, al
;   jne   DoCheckForSharedRAM         ; Something is there.
;   
;   lea   eax, NICNotInSlotMessage
;   jmp   DriverInitResetError          ; Bail out
;

DoCheckForSharedRAM:
        call    CheckForSharedRAM
        jne     DriverInitResetError                    ; Jump if error.

;
;***************************************************************\
;                                                               *
; Let MSM Register the hardware options.                        *
;                                                               *
;***************************************************************/
;
        call    MSMRegisterHardwareOptions
        cmp     eax, 1                                  ; Error Registering?
        ja      DriverInitError                    ; Jump if so.
        je      DriverInitExit                          ; Skip if new frame.

;
;***************************************************************\
;                                                               *
; Set Ethernet registers from Base IO.                          *
;                                                               *
;***************************************************************/
;
        movzx   eax, [ebx].MLIDIOPortsAndLengths        ; EAX = I/O Base.
        cmp     DriverSendPtr, offset DriverSend        ; I/O adapter?
        je      short DriverInitIO                      ; Jump if so.

        mov     eax, [ebx].MLIDMemoryDecode0    ; EAX = Physical Memory addr.
        mov     ecx, eax
        shr     ecx, 13                         ; CL = Memory bits 13-18.
        or      cl, 01000000b                   ; Set memory bit.
        mov     [ebp].Control1Value, cl         ; Save for DriverReset.
        mov     ecx, eax
        shr     ecx, 19
        or      cl, 0c0h
        mov     [ebp].Control2Value, cl

        mov     eax, [ebx].MLIDLinearMemory0            ; EAX = Logical Mem.
        mov     [ebp].NICRAMSegmentBase, eax            ; Store Base.
        xor     ecx, ecx
        mov     ch, RAMPSTART
        add     eax, ecx
        mov     [ebp].NICRAMReceiveRingStart, eax       ; Store Rx Start.
        mov     ch, RAMPSTOP-RAMPSTART
        add     eax, ecx
        mov     [ebp].NICRAMSegmentLimit, eax           ; Store Rx End.

        movzx   eax, [ebx].MLIDIOPortsAndLengths        ; EAX = I/O Base.
        lea     ecx, [eax].IControl1                    ; Base + 00h
        mov     [ebp].Control1, ecx

        lea     ecx, [eax].IControl2                    ; Base + 05h
        mov     [ebp].Control2, ecx

        lea     ecx, [eax].IProm                        ; Base + 08h
        mov     [ebp].Prom, ecx

        mov     [ebp].TransmitPage0, 0h
        mov     [ebp].TransmitPage1, 6h

        add     eax, 10h                                ; Adjust Base I/O.

DriverInitIO:
        lea     ecx, [eax].ICommand                     ; Base + 00h
        mov     [ebp].Command, ecx

        lea     ecx, [eax].IPageStart                   ; Base + 01h
        mov     [ebp].PageStart, ecx

        lea     ecx, [eax].IPageStop                    ; Base + 02h
        mov     [ebp].PageStop, ecx

        lea     ecx, [eax].IBoundary                    ; Base + 03h
        mov     [ebp].Boundary, ecx

        lea     ecx, [eax].ITransmitStatus              ; Base + 04h
        mov     [ebp].TransmitStatus, ecx

        lea     ecx, [eax].ITransmitByteCount0          ; Base + 05h
        mov     [ebp].TransmitByteCount0, ecx

        lea     ecx, [eax].ITransmitByteCount1          ; Base + 06h
        mov     [ebp].TransmitByteCount1, ecx

        lea     ecx, [eax].IInterruptStatus             ; Base + 07h
        mov     [ebp].InterruptStatus, ecx

        lea     ecx, [eax].IRemoteStartAddress0         ; Base + 08h
        mov     [ebp].RemoteStartAddress0, ecx

        lea     ecx, [eax].IRemoteStartAddress1         ; Base + 09h
        mov     [ebp].RemoteStartAddress1, ecx

        lea     ecx, [eax].IRemoteByteCount0            ; Base + 0ah
        mov     [ebp].RemoteByteCount0, ecx

        lea     ecx, [eax].IRemoteByteCount1            ; Base + 0bh
        mov     [ebp].RemoteByteCount1, ecx

        lea     ecx, [eax].IReceiveConfiguration        ; Base + 0ch
        mov     [ebp].ReceiveConfiguration, ecx

        lea     ecx, [eax].ITransmitConfiguration       ; Base + 0dh
        mov     [ebp].TransmitConfiguration, ecx

        lea     ecx, [eax].IDataConfiguration           ; Base + 0eh
        mov     [ebp].DataConfiguration, ecx

        lea     ecx, [eax].IInterruptMask               ; Base + 0fh
        mov     [ebp].InterruptMask, ecx

        lea     ecx, [eax].INICData                     ; Base + 10h
        mov     [ebp].NICData, ecx

        lea     ecx, [eax].IReset                       ; Base + 1fh
        mov     [ebp].Reset, ecx

if BackToBackSends
        mov     [ebp].MSMTxFreeCount, 2         ; Allow 2 transmits simultaneously.
else
        mov     [ebp].MSMTxFreeCount, 1         ; Allow 1 transmit.
endif

        mov     eax, OP_SCOPE_ADAPTER
        call    DriverReset                     ; Initialize NIC.
        jnz     short DriverInitResetError      ; Exit if error reseting.

        mov     [ebp].FirstTimeInit, 0          ; Disable DriverReset from
                                                ; testing the hardware again.
        dec     [ebp].AdapterResetCount         ; Adjust reset count.
;
;***************************************************************\
;                                                               *
; EBX -> Frame Data Space(Config Table).                        *
; EBP -> Adapter Data Space.                                    *
;                                                               *
; Let MSM Set Hardware Interrupt.                               *
;                                                               *
;***************************************************************/
;
        call    MSMSetHardwareInterrupt
        jnz     short DriverInitError                 ; Jump if error.

   call   VerifyIRQ               ; Is hardware interrupt same
   lea   eax, IRQInvalidMessage       ;  as what user told us?
   jnz   short DriverInitResetError    ; No, print error msg

        call    MSMRegisterMLID                 ; Register MLID.
        jnz     short DriverInitError        ; Jump if error.

        mov     eax, 18                         ; Schedule call back in 18 ticks.
        call    MSMScheduleIntTimeCallBack
        jnz     short DriverInitError        ; Jump if error.

DriverInitExit:
   mov   InDriverInit, 0         ;SPD 125034
        xor     eax, eax
        CPop
        ret

DriverInitResetError:
        push    eax                             ; Save error message.
        call    MSMReturnDriverResources        ; Return resources.
        pop     eax                             ; EAX -> Error message.
DriverInitError:
   mov   InDriverInit, 0         ;SPD 125034
        mov     esi, eax                        ; ESI -> Error message.
        call    MSMPrintStringFatal             ; Display message
        or      eax, 1                          ; Do not load return code.
        CPop

        ret

DriverInit      endp

CheckForSharedRAM       proc
;
;***************************************************************\
;                                                               *
; Since ParseDriverParameters has given us the port number,     *
; lets see if we're in I/O or Shared RAM mode.                  *
;                                                               *
;***************************************************************/
;
        mov     DriverSendPtr, offset DriverSend        ; Default to I/O

;SPD 124386 Begin
; Assume for now card is NOT shared RAM, save current value of NICCommand
; register and force to page 0.

   movzx   edx, [ebx].MLIDIOPortsAndLengths
   in   al, dx            
   mov   ch, al               ; CH = current value.
   mov   al, NIC_Page0
   out   dx, al
   SLOW
;SPD 124386 End

        movzx   edx, [ebx].MLIDIOPortsAndLengths        ; DX = Base I/O +
        add     edx, 8                                  ;  RemoteStartAddr.
        in      al, dx                                  ; Get current value.
        mov     ah, al                                  ; Save in AH.
        inc     al
        mov     cl, al                                  ; CL = New value.
        out     dx, al                                  ; Write new value.
        SLOW
        in      al, dx
        cmp     al, cl                                  ; Equal if I/O.
        mov     al, ah                                  ; Restore old value
        out     dx, al                                  ;  in case it wasn't
                                                        ;  really an NE2000.
        mov     AdapterIsSharedRAM, 0
        je      short CheckForMemoryDecode0             ; Jump if I/O.
        mov     AdapterIsSharedRAM, 1

;SPD 124386 Begin
   movzx   edx, [ebx].MLIDIOPortsAndLengths   ; Restore original
   mov   al, ch               ; value.
   out   dx, al
   SLOW

   add   edx, 10h            ; Save NICCommand
   in   al, dx               ; value in case we
   mov   ch, al               ; we need to restore
   mov   al, NIC_Page0            ; it later, then force
   out   dx, al               ; to page 0.
   SLOW

; We know that if an NE2000 is present it is not in IO mode. Now check to
; see if it is even present.

   add   edx, 8               ; DX = RemoteStartAddr
   in   al, dx               ; Read current value.
   mov   ah, al               ; Save it.
   inc   al               ; Create test value. 
   mov   cl, al               ; CL = New value.
   out   dx, al               ; Write new value.
   SLOW               
   in   al, dx
   cmp   al, cl               ; Card present?
   mov   al, ah               ; Restore old value
   out   dx, al               ; in case it wasn't.
   je   CheckForMemoryDecode0         ; Yes, NE2000 is there.

   movzx   edx, [ebx].MLIDIOPortsAndLengths   ; No card so we better
   add   edx, 10               ; restore the old
   mov   al, ch               ; value
   out   dx, al
   SLOW

   jmp   AdapterConfigurationError      ; Bail out.
;SPD 124386 End                                                                                                                                              

CheckForMemoryDecode0:
;
; If user already gave us Shared RAM address, get out.
;

        cmp     [ebx].MLIDMemoryDecode0, 0              ; Shared RAM selected?
        jne     AdapterSharedRAM                        ; Get out if so.
;
; If we're in I/O mode, we're done.
;
        cmp     AdapterIsSharedRAM, 0                   ; I/O mode?
        je      short AdapterIOMode                     ; Get out if so.
;
;***************************************************************\
;                                                               *
; In shared RAM mode and user hasn't given us the shared RAM    *
; address yet. Lets see if we're just adding a new frame type.  *
;                                                               *
;***************************************************************/
;
        lea     esi, IOConfigurationList
        movzx   eax, [ebx].MLIDIOPortsAndLengths
SearchForMatchingIO:
        cmp     [esi].CLink, 0                          ; End of list?
        je      short BuildMemoryList                   ; Jump out if so.
        mov     esi, [esi].CLink                        ; ESI -> next struc.
        cmp     [esi].CIOPortsAndLengths, ax            ; Match?
        jne     SearchForMatchingIO                     ; Jump if not.
AdapterIOMode:
        mov     DriverSendPtr, offset DriverSend   
        xor     eax, eax
        ret
;
;***************************************************************\
;                                                               *
; We are adding a new physical adapter. Build Memory strings    *
; and prompt user for memory address.                           *
;                                                               *
;***************************************************************/
;
BuildMemoryList:
        mov     MemOnCommandLine, 0                     ; Keep count.
        lea     edi, MemPromptValues                    ; EDI -> Buffer.
        lea     edx, MemoryDecode0Data+4                ; EDX -> Values.
        lea     ebp, MemPromptStrings                   ; EBP -> Strings.

BuildMemoryListLoop:
        mov     eax, [edx]                              ; EAX = Mem address.
        mov     ecx, 6
        test    eax, 00f00000h                          ; Over 1 Meg?
        lea     esi, IOConfigurationList
        jne     short SearchForMatchingMem              ; Jump if so.
        mov     ecx, 5
;
; See if anyone else is using it.
;
SearchForMatchingMem:
        cmp     [esi].CLink, 0                          ; End of list?
        je      short AddMemoryToList                   ; Jump if so.
        mov     esi, [esi].CLink                        ; ESI -> next struc.
        cmp     [esi].CMemoryDecode0, eax               ; Being used.
        je      short BuildMemoryNext                   ; Jump if so.
        cmp     [esi].CMemoryDecode1, eax               ; Being used.
        je      short BuildMemoryNext                   ; Jump if so.
        jmp     SearchForMatchingMem

AddMemoryToList:
        cmp     edi, offset MemPromptValues             ; First one?
        jne     short AddMemoryNow                      ; Jump if so.
        push    edi
        push    ecx
        lea     edi, MemDefault
        mov     esi, ebp
        rep     movsb
        pop     ecx
        pop     edi

AddMemoryNow:
        push    ecx
        mov     esi, ebp
        rep     movsb
        pop     ecx

        mov     dword ptr [edi], '   ,'                 ; Add ', '
        cmp     MemOnCommandLine, 5
        jne     short BuildMemoryUpdate
        mov     dword ptr [edi], CR OR (LF SHL 8)
BuildMemoryUpdate:
        add     edi, 2

BuildMemoryNext:
        cmp     eax, 0fe0000h                           ; Last one?
        je      short BuildMemoryAddTail                ; Jump if so.

        add     edx, 4
        add     ebp, ecx
        inc     MemOnCommandLine
        jmp     BuildMemoryListLoop

BuildMemoryAddTail:
        sub     edi, 2                                  ; Overwrite ', '
        lea     esi, MemPromptTail
        mov     ecx, MemPromptTailSize
        rep     movsb

        mov     MemOnCommandLine, 0

        or      NE2000TextLen, T_REQUIRED               ; 'MEM=' required.
ParseForMem:
        mov     InitMem, 0
        lea     esi, DriverParameterBlock               ; ESI -> Parm block.
        call    MSMParseCustomKeywords                  ; Parse for 'MEM='.

        mov     eax, InitMem
        mov     [ebx].MLIDMemoryDecode0, eax
        or      eax, eax
        je      short AdapterConfigurationError         ; Jump if not entered.

        lea     esi, IOConfigurationList
SearchForDupMem:
        cmp     [esi].CLink, 0                          ; End of list?
        je      short CheckForValidMem                  ; Jump if so.
        mov     esi, [esi].CLink                        ; ESI -> next struc.
        cmp     [esi].CMemoryDecode0, eax               ; Being used.
        je      ParseForMem                             ; Jump if so.
        cmp     [esi].CMemoryDecode1, eax               ; Being used.
        je      ParseForMem                             ; Jump if so.
        jmp     SearchForDupMem

CheckForValidMem:
        lea     esi, MemoryDecode0Data+4
        mov     ecx, MemoryDecode0Data
CheckForValidMemLoop:
        cmp     [esi], eax
        je      short AdapterSharedRAM                  ; Jump if valid.
        add     esi, 4
        dec     ecx
        jne     CheckForValidMemLoop
        jmp     ParseForMem

AdapterSharedRAM:
        cmp     AdapterIsSharedRAM, 0                   ; Board config match?
        je      short AdapterConfigurationError         ; Jump if not

        mov     DriverSendPtr, offset DriverSendSharedRAM
        mov     [ebx].MLIDLength0, 400h                 ; Store length of memory.
        xor     eax, eax
        ret

AdapterConfigurationError:
        lea     eax, NICBadConfiguration                ; EAX -> Error Message.
        or      eax, eax
        ret

CheckForSharedRAM       endp

MemRoutine      proc

        mov     InitMem, eax
        mov     MemOnCommandLine, dl
        ret

MemRoutine      endp
        subttl  -- DriverShutdown --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( DriverShutdown, NE2000/API/SHUTDOWN )
;
; Name:         DriverShutdown
;
; Description:  This routine will turn off the NIC.
;
; On Entry:     EAX     set to OP_SCOPE_ADAPTER if the adapter specified by EBP
;         is to be shutdown. Otherwise set to OP_SCOPE_LOGICAL_BOARD
;         which indicates that only the logical board specified
;                        by EBX is to be shutdown.

;               EBX     @ Frame Data Space
;               ECX     0 if Permanent Shutdown
;               EDX     N/A
;               EBP     @ Adapter Data Space
;               ESI     N/A
;               EDI     N/A
;
;               Note:   Interrupts are disabled.
;
; On Return:    EAX     0 if successful
;               EBX     Preserved
;               ECX     Preserved
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Preserved
;               EDI     Preserved
;
;               Flags:
;
;               Note:   Interrupts preserved.
;
; Remarks:      This routine is called by the MSM media module.
;               It is called at process time.
;
; See Also:     ETHERTSM\EtherTSMShutdown
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
DriverShutdown  proc

if DEBUG
        push    eax
        mov     al, 'd'
        call    OutChar
        pop     eax
endif
   cmp   eax, OP_SCOPE_ADAPTER
        jne     DriverShutdownExit

        mov     edx, [ebp].InterruptMask        ; EDX = InterruptMask register.
        mov     al, NIC_MaskByte                ; Unmask all interrupts.
        out     dx, al

        mov     edx, [ebp].Command
        mov     al, 21h
        out     dx, al                          ; Stop the NIC.

        cmp     [ebp].NICRAMSegmentBase, 0      ; Shared RAM?
        je      short DriverShutdownExit        ; Jump if not.

        mov     edx, [ebp].Control1             ; DX = Control 1.
        and     cl, NOT 01000000b               ; Clear memory bit.
        mov     al, [ebp].Control1Value
        out     dx, al                          ; Reset adapter.

DriverShutdownExit:
        xor     eax, eax                        ; Good Return code.
        ret

DriverShutdown  endp
        subttl  -- DriverRemove --
        page
;
;***********************************************************************\
;
; BEGIN_MANUAL_ENTRY( DriverRemove, NE2000/API/REMOVE )
;
; Name:         DriverRemove
;
; Description:  This routine call the MSM to return our resources.
;
; On Entry:     EAX     N/A
;               EBX     N/A
;               ECX     N/A
;               EDX     N/A
;               EBP     N/A
;               ESI     N/A
;               EDI     N/A
;
;               Note:   Interrupts are in any state.
;
; On Return:    EAX     Destroyed
;               EBX     Preserved
;               ECX     Destroyed
;               EDX     Destroyed
;               EBP     Preserved
;               ESI     Preserved
;               EDI     Preserved
;
;               Flags:
;
;               Note:   Interrupts preserved.
;
; Remarks:      This routine is called by the OS at unload.
;               It is called at process time.
;
; See Also:     MSM\MSMDriverRemove
;
; END_MANUAL_ENTRY
;
;***********************************************************************/
;
DriverRemove    proc

        CPush
        mov     eax, DriverModuleHandle
        call    MSMDriverRemove
        CPop
        ret

DriverRemove    endp

OSCODE  ends

        end