adapter.asm

Warning: This file has been marked up for HTML

page ,130
;***********************************************************************
; $version: 1.39
; $date_modified: 121898
; $description: C NE3200 Header file for structures
; $owner: ODI LAN Driver Manager
; Copyright (c) 1991 - 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.
;***********************************************************************
;
; ADAPTER.ASM - NE3200 adapter module.
;
; Written by:   DFS
; Date:      04-20-91
;
; 11-30-94 jcp  Version 3.50    (Post NetWare 4.10 release)
;               - Fixed SPD # 82800: NE3200 fail to reject packets that are
;                 too big.  The maximum data size for ETHERNET is 1500 bytes.
;                 Therefore, the value in the field of Transmit ECB's PacketLength
;                 should be 1500 or less, not 1514.
;
; 12-02-94 jcp  Version 3.50
;               - Fixed SPD # 84935: NE3200 will not allow raw send of frame
;                 with size equal to max.  This bug is caused by odd byte
;                 fragment length (raw send 802.2 frame).  When the firmware
;                 receive a packet to transmit, it checks the fragment length,
;                 if it is odd byte, it adds another byte to the fragment
;                 length field and store the value away.  This extra byte
;                 which stored into the fragment length field has prevented
;                 NE3200 to transmit a raw send packet with size equal to 1514.
;
; 12-06-94 jcp  Version 3.50
;               - Fixed transmit stop for no reason problem.  This problem is
;                 caused by some of the error conditions.  When the firmware
;                 is in one of the error condition, (for example, PacketTxTooBig
;                 condition) it exit without returning Tx buffer back to the
;                 queue.  As soon as it uses up all the available Tx buffer
;                 (totally 4), it stops transmitting til HSM issue a reset
;                 command.
;               - Removed IF-ELSE-ENDIF condition for AdapterProcessesTx,
;                 AdapterProcessesRx, TxQueue, AddPolling, CatchIncomplete,
;                 Channel0 and BusMaster.
;               - Cleaned up codes.
;
; 5 May 1995 8:30   DGM
;   Done repair on Adapter firmware, as seeing problems with version 3.50.
;   Pulled back orig. code 06-28-94 and re-implemented changes and fixes.
;
;   Added code to allow for transmitting the last fragment odd byte if
;   the last fragment is odd. Added equate TX_BUFFER_SZ_MAX   and code at
;   label GetlastFragOddByte.
;
;   Removed loop timers that was added on Peek, Poke, 586 Command
;   completion etc. since this was causing problems with the firmware
;   under heavy load.
;
; 26 May 1995 15:28   DGM
;   Removed loop count at WaitForTCBPeek.
;
;   Added <even> statement in front of all procedures to ensure that they
;   were on word boundaries.
;
;   DO NOT REMOVE THEM OR ELSE IT WILL BREAK THE DRIVER.
;
;   Added packet size check at label <Tx8022Type2> and to take error
;   case if packet is too large.
;
; 07 June 1995 15:28   MPK
;   Added <even> statement in front of MainPollingLoop to ensure it
;   is on a word boundary.
;
;   DO NOT REMOVE THE EVEN STATEMENTS OR IT WILL BREAK THE DRIVER.
;
; 18 July 1995 9:05   TNL
;   Change Rx code to use ECB Rx Status of ECB_REG_DIRECT instead of
;   ECB_DIRECT for a direct packet in accordance with latest spec.
;
; 25 July 1995 3:14   TNL
;   Change lines of code which use "size" directive incorrectly. Pharlap
;   accepts the bad syntax, TASM doesn't.
;
; 07 August 1995 8:51    TNL
;   Undo 18 July 1995 change for backward compatability.
;
; 05 March 1996     12:00 LON
;   Single sourced the NE3200 and the NE3200P. Used existing NE3200
;   base and added the Promiscuous code.
;
; 08 March 1996      2:10 LON
;   A receive frame having a length field of Zero was not detected
;   in CopyPacketToHost. Hence the ECB ends up having a PacketLength
;   of zero. Also when in promiscuous mode, a rcv frame with a bad CRC
;   was not being reported in the ECB status field (SPDs 102228, 119416).
;
; 29 March 1996      1:30 LON
;   Corrected another instance of a zero length receive frame (SPD 102225).
;   This was due to register CX getting corrupted in CheckHostForRCB.
;   Corrected a statistics counter not being incremented for too big
;   frames (SPD 121245). Also corrected a possible memory corruption problem
;   when the 586 receives receives a large frame (over 2048), it may
;   reference the DUMMY_RCB and store into memory using an invalid
;   buffer pointer (SPD 121244).
;
; 26 April 1996      3:30 LON
;   Ne3200 was always failing Testcon Good Promiscuous test (SPD 124003),
;   so the receive code was optimised. Plus further enhancements were
;   done to make the Ne3200 have only one binary file to handle both
;   promiscuous and non-promiscuous functions.
;
; 02 May  1996       9:30 LON
;   Ne3200 was often failing to initialize due to failing to restore
;   register BX after calling InitializeRxBuffers from Initialize586.
;   Also in CopyPacketToHost, a check for a 22 bytes or less frame size
;       caused CopyPacketToHost to incorrectly change the reported data
;   size to be the same as the total frame size. This was corrected
;   (SPD 124342).
;
; 12 June 1996       2:00 LON
;   While preparing to send a RAW 802.3 frame, ReadTCBFromHost failed
;   to evenize the byte count of the RAW frame being sent (SPD 128722).
;
; 22-October-97 JCJ
;       SPD# 168595  If packet size is lesser than 60 bytes that has
;                    to be padded in promiscuous mode.
;
;       SPD# 168702  Check has been introduced in CopyPacketToHost to see
;                    whether frame length field in MAC header differs from
;                    the packet size reported by 82586.
;
; 11-November-97 LON
;       The above two fixes were undone because they caused several subtests
;       in the Good Promiscuous test to fail.
;
; 04-May-98      LON
;       When receiving a frame in promiscuous mode that is less than 60 bytes
;       in size, RPacketLength and RPacketSize must both include the pad
;       bytes (minus the media hdr size) - SPD 191220.
;
;***********************************************************************/
;
.186
   name   NE3200_Adapter

DEBUG         equ    0
SupportJabber      equ   -1

DAVID3         equ   -1

;***********************************************************************\
;                                                                       *
; NE3200 adapter structures.                  *
;                                                                       *
;***********************************************************************/

include ASTRUCT.INC

;***********************************************************************\
;                                                                       *
; NE3200 adapter macros.                  *
;                                                                       *
;***********************************************************************/

include AMACROS.INC

;***********************************************************************\
;                                                                       *
; NE3200 adapter equates.                  *
;                                                                       *
;***********************************************************************/
;
; NE3200 common equates.

if SupportJabber
MAX_JABBER      equ   2048+14
endif

TOTAL_RCBS      equ   10h      ; In AdapterRCBList on host.
TABLE_SIZE      equ   20h
TABLE_MASK      equ   TABLE_SIZE-1
;
; 82586 memory equates.
;
IFNDEF  NE3200P
NUMBER_TX_BUFFERS       equ     4
ELSE
NUMBER_TX_BUFFERS       equ     10
ENDIF

TX_BUFFER_SIZE      equ   1514
MAX_MULTICAST_ADDR   equ   20      ; Max addresses allowed.


ifdef   DAVID3
MIN_BMIC_XFER_SZ   equ   6+2      ; +2 to make eight

;allow for min frag of 6

TX_BUFFER_SZ_MAX   equ   TX_BUFFER_SIZE + MIN_BMIC_XFER_SZ   
endif


;
MEMORY_586_START   equ   8000h      ; Start at second 32K.
MEMORY_SIZE      equ   0ffffh - 7fffh   ; Size of 82586 RAM.
PROM_OFFSET      equ   4000h      ; PROM Node Address.
DUMMY_RCB      equ   MEMORY_586_START; Resolves a 586 D-Step Errata.
DUMMY_BUFFER      equ   DUMMY_RCB + size BDStruct ; 16 byte buffer.

SCB         equ   DUMMY_BUFFER + 16
ISCP         equ    SCB + size SCBStructure
COMMAND_BUFFER      equ   ISCP + 8
TX_BUFFERS      equ   COMMAND_BUFFER + 32 + (MAX_MULTICAST_ADDR * 6)

ifdef   DAVID3
RX_BUFFERS   equ   TX_BUFFERS+(NUMBER_TX_BUFFERS*(TX_BUFFER_SZ_MAX + size TxCStruct + size BDStruct))
else
RX_BUFFERS   equ   TX_BUFFERS+(NUMBER_TX_BUFFERS*(TX_BUFFER_SIZE + size TxCStruct + size BDStruct))
endif

SCP         equ   0FFF6h      ; SCP.
MEMORY_END      equ   SCP      ; End at SCP.
SCP_LENGTH      equ   10
ISCP_LENGTH      equ   8
;
; 82586 equates.
;
CA_PORT         equ   180h      ; 82586 Channel Attention.
RESET_PORT      equ   100h      ; 82586 Reset port.
MAX_TX_RETRIES      equ   10      ; Maximum Transmit Retries.
CB_MC_SETUP      equ    0003h      ; Set MC command.
ELBIT         equ   8000h      ; Last command in CBL.
CX_INTERRUPT      equ   8000h      ; Tx Interrupt Bit.
FR_INTERRUPT      equ   4000h      ; Rx Interrupt Bit.
TX_COMPLETE      equ   8000h      ; Transmit complete Bit.
TX_BUSY         equ   4000h      ; Transmit busy Bit.
TX_OK         equ   2000h      ; Transmit successful Bit.
CARRIER_SENSE_ERROR   equ   0400h      ; Tx Error-Carrier sense loss.
CLEAR_TO_SEND_ERROR   equ   0200h      ; Tx Error-Clear To Send.
UNDERRUN_ERROR      equ   0100h      ; Tx Error-Underrun.
TX_DEFERED_ERROR   equ   0080h      ; Tx Error-Tx defered.
MAX_COLLISION_ERROR   equ   0020h      ; Tx Error-Max collisions.
CRC_ERROR      equ   0800h      ; Rx Error-CRC.
ALIGN_ERROR      equ   0400h      ; Rx Error-Frame Alignment.
NO_RESOURCE_ERROR   equ   0200h      ; Rx Error-Out of Resources.
OVERRUN_ERROR      equ   0100h      ; Rx Error-DMA overrun.
RUNT_ERROR      equ   0080h      ; Rx Error-too short.
NO_EOF_ERROR      equ   0040h      ; Rx Error-no eof flag.
MAX_FRAGMENTS      equ   16      ; Max ECB Fragments.
MAX_ECB_SIZE      equ   SIZE ATransmitBufferStructure + (MAX_FRAGMENTS*8)
;
; Abend codes.
; Currently, only initialization abend(5) is being used.
;
TRANSFER_HANG_ERROR   equ   1
ODD_ADDRESS_ERROR   equ   2
INVALID_PACKET_LENGTH   equ   3
CHANNEL_ONE_HANG   equ   4
BAD_MEMORY_ERROR   equ   5
ODD_ADDRESS_DEBUG   equ   6
TOO_MANY_FRAGMENTS   equ   7
BAD_TRANSFER_ERROR   equ   8
BAD_ROM_CHECKSUM   equ   9
;
; BMIC equates.
;
BMIC_DATA      equ   200h      ; BMIC Data port.
BMIC_INDEX      equ   202h      ; BMIC Index port.
BMIC_STATUS      equ   204h      ; BMIC Status control port.
AUTO_INC      equ   80h      ; Index Auto Increment bit.
;
; Bit values for BMIC_STATUS.
;
LOCAL_INT_PENDING   equ   80h
CH1_INT_PENDING      equ   40h
CH0_INT_PENDING      equ   20h
LOCAL_INT_ENABLED   equ   10h
LOCAL_INT_ACTIVE   equ   08h
PEEK_POKE_PENDING   equ   04h
CH1_BASE_BUSY      equ   02h
CH0_BASE_BUSY      equ   01h
;
; Mailbox index registers.
;
MAIL_0         equ   10h      ; BMIC Mailbox 0.
MAIL_4         equ   14h      ; BMIC Mailbox 4.
MAIL_8         equ   18h      ; BMIC Mailbox 8.
MAIL_C         equ   1Ch      ; BMIC Mailbox 0C.
;
ABEND_MAILBOX      equ   MAIL_0      ; BMIC Mailbox 0(init only).
IDLE_MAILBOX      equ   MAIL_0 + 0   ; BMIC Mailbox 0.
PARM_UPDATE_MAILBOX   equ   MAIL_0 + 1   ; BMIC Mailbox 1.
STAT_UPDATE_MAILBOX   equ   MAIL_0 + 2   ; BMIC Mailbox 2.
VALID_TCB_MAILBOX   equ   MAIL_0 + 3   ; BMIC Mailbox 3.
TCB_MAILBOX      equ   MAIL_4      ; BMIC Mailbox 4.
POLLING_MAILBOX      equ   MAIL_4      ; BMIC Mailbox 4.
PARAMETER_MAILBOX   equ   MAIL_C      ; BMIC Mailbox 0C.
;
; Channel 0 index registers
;
CH0_BASE_COUNT      equ   40h
CH0_BASE_ADDR      equ   43h
CH0_CONFIG      equ   48h
CH0_STROBE      equ   49h
CH0_STATUS      equ   4Ah
CH0_TBI_BASE_ADDR   equ   4Bh
;
; Channel 1 index registers
;
CH1_BASE_COUNT      equ   60h
CH1_BASE_ADDR      equ   63h
CH1_CONFIG      equ   68h
CH1_STROBE      equ   69h
CH1_STATUS      equ   6Ah
CH1_TBI_BASE_ADDR   equ   6Bh
;
; Bit values for CHx_BASE_COUNT+2
; 
AUTO_START      equ   80h
ADAPTER_TO_EISA      equ   40h
;
; Bit values for CHx_STATUS
;
FIFO_STALLED      equ   10h
TRANSFER_ENABLED   equ   08h
TRANSFER_IN_PROGRESS   equ   04h
TRANSFER_TERMINATED   equ   02h
TRANSFER_COMPLETE   equ   01h
;
; Peek/Poke index registers.
;
PEEK_POKE_DATA      equ   30h
PEEK_POKE_ADDR      equ   34h
PEEK_POKE_CONTROL   equ   38h
;
; EISA Doorbell index registers.
;
EISA_DOORBELL_ENABLE   equ   0eh
EISA_DOORBELL_STATUS   equ   0fh
;
; Bit values for EISA Doorbell registers.
;
TX_COMPLETE_BIT      equ   01h
;
; 80186 I/O ports. (Note: The Adapter.asm code is run on the adapter's 80186)
;
PACSReg         equ   0ffa4h      ; Peripheral chip-select addr.
MPCSReg         equ   0ffa8h      ; Peripheral chip-select mode.

DMA0Control      equ   0ffcah      ; DMA 0 Control Word.
DMA0Count      equ   0ffc8h      ; DMA 0 Transfer Count.
DMA0DestHi      equ   0ffc6h      ; DMA 0 Dest Ptr upper 4 bits.
DMA0DestLo      equ   0ffc4h      ; DMA 0 Dest Ptr low 16 bits.
DMA0SourceHi      equ   0ffc2h      ; DMA 0 Src Ptr upper 4 bits.
DMA0SourceLo      equ   0ffc0h      ; DMA 0 Src Ptr low 4 bits.

DMA1Control      equ   0ffdah      ; DMA 1 Control Word.
DMA1Count      equ   0ffd8h      ; DMA 1 Transfer Count.
DMA1DestHi      equ   0ffd6h      ; DMA 1 Dest Ptr upper 4 bits.
DMA1DestLo      equ   0ffd4h      ; DMA 1 Dest Ptr low 16 bits.
DMA1SourceHi      equ   0ffd2h      ; DMA 1 Src Ptr upper 4 bits.
DMA1SourceLo      equ   0ffd0h      ; DMA 1 Src Ptr low 4 bits.

T0Control      equ   0ff56h      ; Timer 0 Control Word.
T0MaxCountB      equ   0ff54h      ; Timer 0 Max Count B.
T0MaxCountA      equ   0ff52h      ; Timer 0 Max Count A.
T0Count         equ   0ff50h      ; Timer 0 Count reg.

T1Control      equ   0ff5eh      ; Timer 1 Control Word.
T1MaxCountB      equ   0ff5ch      ; Timer 1 Max Count B.
T1MaxCountA      equ   0ff5ah      ; Timer 1 Max Count A.
T1Count         equ   0ff58h      ; Timer 1 Count reg.

T2Control      equ   0ff66h      ; Timer 2 Control Word.
T2MaxCount      equ   0ff62h      ; Timer 2 Max Count A.
T2Count         equ   0ff60h      ; Timer 2 Count reg.
;
; Rx Destination Address Type (First byte of RDriverWorkSpace).
;
ECB_DIRECT      equ   0000h      ; Physical destination address.
ECB_MULTICAST      equ   0001h      ; Multicast destination address.
ECB_BROADCAST      equ   0002h      ; Broadcast destination address.
ECB_REMOTE_UNICAST   equ   0004h      ; Remote Unicast dest addr.
ECB_REMOTE_MULTICAST   equ   0008h      ; Remote Multicast dest addr.
ECB_NO_SROUTE      equ   0010h      ; ROUTE.NLM not loaded.
ECB_ERR_PKT      equ   0020h      ; Frame is malformed, etc.
ECB_REG_DIRECT      equ   0080h      ; Register for Direct frames.
ECB_PROMISCUOUS      equ   ECB_REG_DIRECT OR ECB_MULTICAST OR ECB_BROADCAST OR ECB_REMOTE_UNICAST OR ECB_REMOTE_MULTICAST OR ECB_NO_SROUTE

CODE   segment   public   word 'CODE'
   assume   CS:CODE, DS:CODE, ES:CODE, SS:CODE

   jmp   Start            ; Jump to code.

   org   100h            ; Skip over vector table.
;
;***********************************************************************\
;                                                                       *
; Static Data variables.                  *
;                                                                       *
;***********************************************************************/
;
;
;***************************************************************\
;                                                               *
; Adapter Parameter Block. Do not change the order of the   *
; following 12 variables.               *
;                                                               *
;***************************************************************/
;
   even               ; Must be even for BMIC.
AdapterStructure      label   byte
LogicalToPhysical      dw   2 dup (0)
NodeAddressPointer      dw   2 dup (0)
BoardNumber8023         dw   -1
BoardNumberEII         dw   -1
BoardNumber8022         dw   -1
BoardNumberSNAP         dw   -1
MaxReceivePacketLength      dw   2 dup (0)
GenericStatisticsPointer   dw   2 dup (0)
CustomStatisticsCount      dw   0
RCBListPointer         dw   2 dup (0)   ; pts to AdapterRCBList.
MulticastCount         dw   0
MulticastListPointer      dw   2 dup (0)
HostNodeAddress         db   6 dup (0)
PromiscuousMode         db   0
PollTimeout         dw   0      ;#
            db   3 dup (0)   ;# dword align
IF DEBUG
DiagnosticBufferPointer      dw   2 dup (0)   ; Diagnostics buffer
ENDIF
AdapterStructureSize      equ   $ - AdapterStructure

NextEmptyRCB         dw   2 dup (0)
TCBListPointer         dw   2 dup (0)
TCBIndex         dw   0

   even         ; Must be even for BMIC.
BoardID            db   6 dup (0) ; Individual (MAC) Node Adr.

HostParameterBlock      dw   2 dup (0)

RCBListBuffer         dw   10h * 2 dup (0)   ; TOTAL_RCBS = 10.
KnownRCBCount         dw   1

SixteenBytesOfZero      db   4 dup (00h)
TwelveBytesOfZeroPlusSignature   db   12 dup (00h)
            db   4  dup (11h)
InitialSCP         db   0
            db   5 dup (0)
            dw   ISCP
            dw   0
            dw   1      ; Initial ISCP.
            dw   SCB
            dw   0, 0

ConfigLoopback      dw   0,8002h,0,080Ch,6640h,6000h,0F200h,00h,40h

ConfigNormal      dw   0,8002h,0,080Ch,2640h,6000h,0F200h,00h,40h

ConfigNormalSize     equ   $ - ConfigNormal   ;TNL 7/25/95
IFNDEF  NE3200P

;;ConfigPromiscuous   dw   0,8002h,0,080Ch,2640h,6000h,0F200h,01h,20
ConfigPromiscuous   dw   0,8002h,0,080Ch,26C0h,6000h,0F200h,01h,14

ELSE
ConfigPromiscuous       dw      0,8002h,0,080Ch,26C0h,6000h,0F200h,01h,14
               ; 14 = MinFrmSize 82586 can capture.
ENDIF

DeadMan            dw   0
DeadManHigh         dw   35   ; Max deadman (~60 seconds).
;;FragLength         dw   0

IFDEF NE3200P
;
;***************************************************************\
;                                                               *
; Receive ECB structure for Ne3200Promisc. code.      *
;                                                               *
;***************************************************************/
;

SaveRDBOffset                   dw      0

;---------------------------------------------------------------\
;                        *
; The Ne3200Promisc design requires only one RCB Template (which*
; is copied up to the host driver to fill in the real RCB(ECB)).*
; This is better than the Ne3200 design since its UniReceiveBuf *
; structure has an RCB template for every received frame. This   *
; wastes memory.                  *
;                        *
;---------------------------------------------------------------/

   even
RCB_TEMPLATE         AReceiveBufferStructure         <>
RCVMACDest         db      6 dup (0)   ; Keep these fields
RCVMACSource         db      6 dup (0)   ;  in order.
RCVMACLength         dw      0
TEMPLATE_MAC_SIZE      equ     size AReceiveBufferStructure + 14
      
ENDIF


;***************************************************************\
;                                                               *
; Multicast Table variables.               *
;                                                               *
;***************************************************************/
;
   even            ; Must be even for BMIC.
HostMulticastTable      db   (MAX_MULTICAST_ADDR * 8) dup (0)
AdapterMulticastLocation   dw   0

;***************************************************************\
;                                                               *
; Local Transmit buffer variables.            *
;                                                               *
;***************************************************************/
;
TxFreeHead         dw   0
TxCHead            dw   0
TxCEnd            dw   0
CurrentTCB         dw   0
OddByteStorage         dw   0

;***************************************************************\
;                                                               *
; Local Receive buffer variables.            *
;                                                               *
;***************************************************************/
;
RCBIndex         dw   0
FDHead            dw   0
FDEnd            dw   0
RcvFrameSize         dw   0

IFDEF   NE3200P
RBDHead                         dw      0
RBDEnd                          dw      0
RBDLast                         dw      0
RBDTotalSize                    dw      0
RBDWrappedFlag                  dw      0
RBDSizeBeforeWrap               dw      0
RBDSizeAfterWrap                dw      0
BufferHead                      dw      0
ENDIF

;***************************************************************\
;                        *
; NOTE: The 7 Sequence Buffers below are presetup values used   *
; to program the BMIC I/O port registers to perform a specific   *
; byte(s) transfer between us (the adapter or firmware) and the   *
; Host HSM driver.                  *
;                        *
;***************************************************************/


;***************************************************************\
;                                                               *
; To Host Node Addr(IA) Sequence Buffer. Don't change the order.*
;                                                               *
;***************************************************************/
;
NodeAddrTransferSequence   label   byte
NodeAddrTransferCount      dw   6
            db   AUTO_START OR ADAPTER_TO_EISA
NodeAddrTransferOffsetLo   dw   ?
NodeAddrTransferOffsetHi   dw   ?

;***************************************************************\
;                                                               *
; From Host Parameter Sequence Buffer. Do not change the order.   *
;                                                               *
;***************************************************************/
;
ParameterTransferSequence   label   byte
ParameterLocalAddress      dw   ?
ParameterTransferCount      dw   AdapterStructureSize
            db   AUTO_START
ParameterTransferOffsetLo   dw   ?
ParameterTransferOffsetHi   dw   ?

;***************************************************************\
;                                                               *
; From Host RCB List Sequence Buffer. Do not change the order.   *
; Copy down the host driver's AdapterRCBList (ECB) ptr table.   *
;                                                               *
;***************************************************************/
;

RCBListTransferSequence      label   byte
RCBListLocalAddress      dw   ?
RCBListTransferCount      dw   10h * 4      ; TOTAL_RCBS = 10h.
            db   AUTO_START
RCBListTransferOffsetLo      dw   ?   ; Host adr of AdapterRCBList.
RCBListTransferOffsetHi      dw   ?

;***************************************************************\
;                                                               *
; Read TCB Transfer Sequence Buffer. Do not change the order.   *
; Copy down the host driver's AdapterTCBList (ECB) ptr table.   *
;                                                               *
;***************************************************************/
;
ReadTCBTransferSequence      label   byte
ReadTCBLocalAddress      dw   ?
ReadTCBTransferCount      dw   MAX_ECB_SIZE
            db   AUTO_START
ReadTCBTransferOffsetLo      dw   ?
ReadTCBTransferOffsetHi      dw   ?

;***************************************************************\
;                                                               *
; To Host Statistic Sequence Buffers. Do not change the order.   *
;                                                               *
;***************************************************************/
;
StatisticsSequence      label   byte   ; First sequence
StatisticsLocalAddress      dw   ?
            ; byte offset below is used as a byte cnt.
            dw   TotalTxOKByteCountLow
StatisticsDir         db   AUTO_START OR ADAPTER_TO_EISA
StatisticsOffsetLo      dw   ?
StatisticsOffsetHi      dw   ?


StatisticsSequence1      label   byte   ; Second sequence
StatisticsLocalAddress1      dw   ?
            dw   EndOfStats - TxOKSingleCollision
StatisticsDir1         db   AUTO_START OR ADAPTER_TO_EISA
StatisticsOffsetLo1      dw   ?
StatisticsOffsetHi1      dw   ?

;***************************************************************\
;                                                               *
; From Host MC Table Sequence Buffer. Do not change the order.   *
;                                                               *
;***************************************************************/
;
MCTableSequence         label   byte
MCTableLocalAddress      dw   ?
MCTableCount         dw   0
            db   AUTO_START
MCTableOffsetLo         dw   ?
MCTableOffsetHi         dw   ?

;***************************************************************\
;                                                               *
; To Host Diagnostic Sequence Buffer. Do not change the order.   *
;                                                               *
;***************************************************************/
;
IF DEBUG
DiagnosticSequence      label   byte
DiagnosticAddress      dw   ?
DiagnosticTransferCount      dw   10h
            db   AUTO_START OR ADAPTER_TO_EISA
DiagnosticOffsetLo      dw   ?
DiagnosticOffsetHi      dw   ?

;;DiagnosticSequence      label   byte
;;DiagnosticAddress      dw   0   ;Lower 16k RAM
;;DiagnosticTransferCount   dw   4000h
;;            db   AUTO_START OR ADAPTER_TO_EISA
;;DiagnosticOffsetLo      dw   ?
;;DiagnosticOffsetHi      dw   ?
;;
;;DiagnosticSequence1      label   byte
;;DiagnosticAddress1      dw   8000h/2   ;Upper 32k RAM
;;DiagnosticTransferCount1   dw   8000h
;;            db   AUTO_START OR ADAPTER_TO_EISA
;;DiagnosticOffsetLo1      dw   ?
;;DiagnosticOffsetHi1      dw   ?
   even
DiagBuffer         db   10h dup (' ')
            db   ?    ;for '*'
CurDiagOffset         dw   0
HostDiagSegCount      dw   1000h/10h
PollLoopCount         dw   4000h
ENDIF


;***************************************************************\
;                                                               *
; Statistic counter buffer.               *
;                                                               *
;***************************************************************/

   even            ; Must be even for BMIC.
StatisticCounters   StatisticStructure <>

   even
TransmitECB   db   MAX_ECB_SIZE dup (0)


   public   AdapterStructure
   public   LogicalToPhysical
   public   NodeAddressPointer
   public   BoardNumber8023
   public   BoardNumberEII
   public   BoardNumber8022
   public   BoardNumberSNAP
   public   MaxReceivePacketLength
   public   GenericStatisticsPointer
   public   CustomStatisticsCount
   public   RCBListPointer
   public   MulticastCount
   public   MulticastListPointer
   public   HostNodeAddress
   public   PromiscuousMode
   public   PollTimeout

IF DEBUG
   public   DiagnosticBufferPointer
ENDIF

   public   NextEmptyRCB
   public   TCBListPointer
   public   TCBIndex
   public   BoardID
   public   HostParameterBlock
   public   RCBListBuffer
   public   KnownRCBCount
   public   SixteenBytesOfZero
   public   TwelveBytesOfZeroPlusSignature
   public   InitialSCP
   public   ConfigLoopback
   public   ConfigNormal
   public   ConfigPromiscuous
   public   DeadMan
   public   DeadManHigh
;;   public   FragLength
   public   HostMulticastTable
   public   AdapterMulticastLocation
   public   TxFreeHead
   public   TxCHead
   public   TxCEnd
   public   CurrentTCB
   public   OddByteStorage
   public   RCBIndex
   public   FDHead
   public   FDEnd
   public   NodeAddrTransferSequence
   public   NodeAddrTransferCount
   public   NodeAddrTransferOffsetLo
   public   NodeAddrTransferOffsetHi
   public   ParameterTransferSequence
   public   ParameterLocalAddress
   public   ParameterTransferCount
   public   ParameterTransferOffsetLo
   public   ParameterTransferOffsetHi
   public   RCBListTransferSequence
   public   RCBListLocalAddress
   public   RCBListTransferCount
   public   RCBListTransferOffsetLo
   public   RCBListTransferOffsetHi
   public   ReadTCBTransferSequence
   public   ReadTCBLocalAddress
   public   ReadTCBTransferCount
   public   ReadTCBTransferOffsetLo
   public   ReadTCBTransferOffsetHi
   public   StatisticsSequence
   public   StatisticsLocalAddress
   public   StatisticsDir
   public   StatisticsOffsetLo
   public   StatisticsOffsetHi
   public   StatisticsSequence1
   public   StatisticsLocalAddress1
   public   StatisticsDir1
   public   StatisticsOffsetLo1
   public   StatisticsOffsetHi1
   public   MCTableSequence
   public   MCTableLocalAddress
   public   MCTableCount
   public   MCTableOffsetLo
   public   MCTableOffsetHi

IF DEBUG
   public   DiagnosticSequence
   public   DiagnosticAddress
   public   DiagnosticTransferCount
   public   DiagnosticOffsetLo
   public   DiagnosticOffsetHi

   public   DiagBuffer
   public   CurDiagOffset
   public   HostDiagSegCount
   public   PollLoopCount
ENDIF

   public   StatisticCounters
   public   TransmitECB

   public   Start
   public   MainIdleCommand
   public   Tx8022
   public   Tx8022Aware
   public   Tx8022Type2
   public   TxSNAP
   public   SendPacketOK2
   public   HandleOddLengthFragments
   public   ReadTCBFromHost
   public   SendPacketOK3
   public   TxRawSend
   public   EverythingDone
   public   AddToList
;;   public   FragLengthError
   public   CopyFragmentsOverLoop
   public   FragmentAdjustedForMin
   public   RxMalformedInPromisc
   public   RxMalformedPacket
   public   RxDontInterruptHost
   public   RxCheckMulticastTable
   public   Rx8022
   public   Rx8022SNAP
   public   UpdateHostStatistics
   public   SetupForMulticastCommand
   public   Abend   
   public   Initialize586
   public   Configure586
   public   InitializeTxBuffers
   public   InitializeRxBuffers
   public   Command586NoRestart
   public   Command586
   public   ReStart586
   public   Poll586
   public   ProcessCXInterrupt

   public   CopyPacketToHost
   public   RxEthernetII


;***********************************************************************\
;                                                                       *
; Start - This routine sets up the segment registers, the stack, tests   *
; memory, reads the host parameters, fills in the hosts node address   *
; field, sets up the BMIC and jumps to the main loop.         *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         (Never returns)         *
;   BX = Not Used                     *
;   CX = Not Used                     *
;   DX = Not Used                     *
;    BP = Not Used                     *
;   SI = Not Used                     *
;   DI = Not Used                     *
;                                                                       *
;***********************************************************************/

   even
Start   proc

;---------------------------------------------------------------\
;                                                               *
; Set the flags, segment registers, stack pointer and turn off   *
; the prom to recover the last 32k for our data space.      *
;                                                               *
;---------------------------------------------------------------/

   cli               ; Disable system interrupts
   cld               ; set direction forward.

   xor   ax, ax            ; Set all segment registers
   mov   dx, ax            ; to zero.
   mov   es, ax
   mov   ss, ax

   mov   sp, 4000h         ; Set stack pointer to top
                  ; of 32k code space.
   out   80h, al            ; Turn off PROM.

;---------------------------------------------------------------\
;                                                               *
; Test static RAM for retention (Test for all ones and zeros).   *
;                                                               *
;---------------------------------------------------------------/

   mov   ax, -1            ; AX = 0FFFFh
   mov   bx, es            ; BX = 00000h
MemoryTest1Loop:
   mov   di, MEMORY_586_START      ; DI -> second 32k of memory.
   mov   cx, MEMORY_SIZE / 4      ; CX = SIZE / 4

WriteMemoryLoop:
   stosw               ; Store AX pattern.
   xchg   bx, ax
   stosw               ; Store BX pattern.
   xchg   bx, ax
   loop   WriteMemoryLoop         ; Store next 4 bytes.

   mov   si, MEMORY_586_START      ; SI -> second 32k of memory.
   mov   cx, MEMORY_SIZE / 4      ; CX = SIZE / 4
   mov   dx, ax            ; DX = AX pattern.

ReadMemoryLoop:
   lodsw               ; AX = first pattern.
   cmp   ax, dx            ; Same as written pattern?
   jnz   BadMemoryError         ; Jump if not.

   lodsw               ; AX = second pattern.
   cmp   ax, bx            ; Same as written pattern?
   jnz   BadMemoryError         ; Jump if not.
   loop   ReadMemoryLoop         ; Check next 4 bytes.

   cmp   dx, -1            ; First time thru?
   jne   MemoryTest2         ; Jump if not.

   xchg   bx, dx            ; BX = 0FFFFh
   mov   ax, dx            ; AX = 00000h
   jmp   short MemoryTest1Loop      ; Run test again.

BadMemoryError:
   mov   bx, BAD_MEMORY_ERROR      ; BX = Error value.
   jmp   Abend            ; Halt initialization.

;---------------------------------------------------------------\
;                                                               *
; Test static RAM for opens and shorts.            *
;                                                               *
;---------------------------------------------------------------/

MemoryTest2:
   mov   ax, -1            ; AX = 0FFFFh
   mov   bx, ax            ; BX = 0FFFFh
MemoryTest2Loop:
   mov   si, MEMORY_586_START      ; SI -> second 32k of memory.
   mov   di, si            ; DI -> second 32k of memory.
   mov   cx, MEMORY_SIZE / 2      ; CX = SIZE / 2

MemoryTest2Again:
   stosw               ; Write pattern.
   lodsw               ; Read pattern.
   cmp   ax, bx            ; Patterns match?
   jnz   BadMemoryError         ; Jump if not.

   loop   MemoryTest2Again      ; Test next 2 bytes.

   cmp   ax, -1            ; First time thru?
   mov   ax, 0            ; Set up for new pattern
   mov   bx, ax            ; of zero.
   jne   MemoryTest2Loop         ; Jump if first time thru.

;---------------------------------------------------------------\
;                                                               *
; Zero out all the mailbox registers to signal the driver that   *
; I am alive and well.                  *
;                                                               *
;---------------------------------------------------------------/

   mov   dx, BMIC_INDEX
   mov   al, CH1_CONFIG
   out   dx, al

   mov   dx, BMIC_DATA
   mov   al, 98h
   out   dx, al

   mov   dx, BMIC_INDEX         ; Set index to first
   mov   al, MAIL_0 OR AUTO_INC      ; mailbox.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data port.
   mov   si, offset TwelveBytesOfZeroPlusSignature
   mov   cx, 16
 rep   outsb               ; Send out 16 bytes.

;---------------------------------------------------------------\
;                                                               *
; Wait for the HSM to pass the physical address of its       *
; adapter parameter block.               *
;                                                               *
;---------------------------------------------------------------/

   mov   dx, BMIC_INDEX         ; Set index to parameter
   mov   al, PARM_UPDATE_MAILBOX      ; update mailbox.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data port.
WaitForParameterBlock:
   in   al, dx            ; AL = mailbox value.
   or   al, al            ; Did HSM send us Parm Blk adr?
   jz   WaitForParameterBlock      ; Jump if not.

   mov   dx, BMIC_INDEX         ; Set index to parameter
   mov   al, PARAMETER_MAILBOX OR AUTO_INC   ; mailbox.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data port.
   mov   di, OFFSET ParameterTransferOffsetLo ; DI -> destination.
   mov   cx, 4            ; Read 4 bytes of Host's
 rep   insb               ;  Parameter Block address.

   mov   ax, offset AdapterStructure
   shr   ax, 1            ; Adjust byte adr for BMIC.
   mov   ParameterLocalAddress, ax   ; Save word address.

   mov   ax, offset HostMulticastTable
   shr   ax, 1            ; Adjust for BMIC.
   mov   MCTableLocalAddress, ax      ; Store it.

   mov   ax, offset RCBListBuffer
   shr   ax, 1            ; Adjust for BMIC.
   mov   RCBListLocalAddress, ax      ; Store it.

;---------------------------------------------------------------\
;                                                               *
; Read in the entire adapter parameter block.         *
;                                                               *
;---------------------------------------------------------------/

   call   UpdateParameters      ; Read block from host HSM.

;---------------------------------------------------------------\
;                                                               *
; Read ethernet node address (IA) from PROM or host and save it.*
;                                                               *
;---------------------------------------------------------------/

   mov   di, OFFSET BoardID      ; DI -> our data section.
   mov   cx, 6            ; Read 6 bytes of IA.
   cmp   word ptr HostNodeAddress, 0ffffh
   jne   NodeAddressOverride      ; Jump if no override.
   mov   si, PROM_OFFSET         ; SI -> 0:4000h
CopyBoardIDLoop:
   movsb               ; Copy a byte.
   inc   si            ; Skip a byte.
   loop   CopyBoardIDLoop         ; Copy next byte.
   jmp   short NodeAddressCopied      ; Jump when finished.
NodeAddressOverride:
   mov   si, offset HostNodeAddress   ; Use Local Administered addr.
 rep   movsb
NodeAddressCopied:

;---------------------------------------------------------------\
;                                                               *
; Send node (ethernet MAC) address to the host.         *
;                                                               *
;---------------------------------------------------------------/

   mov   ax, NodeAddressPointer + 0   ; AX = Dest lo.
   mov   NodeAddrTransferOffsetLo, ax   ; Store Dest Lo.
   mov   ax, NodeAddressPointer + 2   ; BX = Dest hi.
   mov   NodeAddrTransferOffsetHi, ax   ; Store Dest Hi.

   mov   di, offset BoardID         ; DI -> Source.
   mov   si, offset NodeAddrTransferSequence   ; SI -> Destination.

   BMICTransfer

;---------------------------------------------------------------\
;                                                               *
; Clear all mailbox registers informing host (HSM driver) that   *
; it can continue with its initialization.         *
;                                                               *
;---------------------------------------------------------------/

   mov   dx, BMIC_INDEX         ; Set index to first mailbox
   mov   al, MAIL_0 OR AUTO_INC      ; register.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   si, offset SixteenBytesOfZero   ; SI -> Sixteen zeros.
   mov   cx, 16            ; Write 16 bytes.
 rep   outsb

;---------------------------------------------------------------\
;                                                               *
; Read the host's RCBList (i.e. AdapterRCBList) to get physical   *
; address of first RCB (or ECB); the physical adr contained   *
; in AdapterRCBList works as a circular queue (see ne3200.386).   *
;                                                               *
;---------------------------------------------------------------/

   mov   di, offset NextEmptyRCB      ; DI -> Dest.
   mov   si, offset RCBListPointer   ; SI = Src location in HSM.
   PeekFromHost
   mov   ax, RCBListPointer      ; low
   mov   bx, RCBListPointer+2      ; hi adr

   add   ax, TABLE_SIZE*4      ; This assumes that the host
   adc   bx, 0            ;  AdapterTCBList is directly
   mov   TCBListPointer, ax      ;  after the AdapterTCBList.
   mov   TCBListPointer+2, bx      ;

;---------------------------------------------------------------\
;                                                               *
; Fixup statistic sequence buffer addresses and copy down part   *
; of the Host's Statistic table.            *
;                                                               *
;---------------------------------------------------------------/

   mov   ax, CustomStatisticsCount   ; AX = Count field.
   mov   StatisticCounters.CustomCounterCount, ax
   mov   ax, offset StatisticCounters   ; AX -> counters.
   shr   ax, 1            ; Adjust for BMIC.
   mov   StatisticsLocalAddress, ax
   mov   ax, offset StatisticCounters + TxOKSingleCollision
   shr   ax, 1
   mov   StatisticsLocalAddress1, ax
   mov   ax, GenericStatisticsPointer   ; Save the address of the
   mov   StatisticsOffsetLo, ax      ;  host's stat table.
   add   ax, TxOKSingleCollision
   mov   StatisticsOffsetLo1, ax
   mov   ax, GenericStatisticsPointer + 2
   mov   StatisticsOffsetHi, ax
   adc   ax, 0
   mov   StatisticsOffsetHi1, ax

   and   StatisticsDir, NOT ADAPTER_TO_EISA ; Copy from host to adapter.
   and   StatisticsDir1, NOT ADAPTER_TO_EISA
   mov   si, offset StatisticsSequence   ; SI -> Preset sequence.
   BMICTransferWithLocal
   mov   si, offset StatisticsSequence1
   BMICTransferWithLocal
   or   StatisticsDir, ADAPTER_TO_EISA
   or   StatisticsDir1, ADAPTER_TO_EISA
   mov   ax, RCBListPointer
   mov   RCBListTransferOffsetLo, ax
   mov   ax, RCBListPointer+2
   mov   RCBListTransferOffsetHi, ax

IF DEBUG
   mov   ax, offset DiagBuffer
   shr   ax, 1
   mov   DiagnosticAddress, ax

   mov   ax, DiagnosticBufferPointer
   mov   DiagnosticOffsetLo, ax
   mov   ax, DiagnosticBufferPointer+2
   mov   DiagnosticOffsetHi, ax
ENDIF

;---------------------------------------------------------------\
;                                                               *
; Fixup multicast sequence buffer addresses.         *
;                                                               *
;---------------------------------------------------------------/

   mov   ax, MulticastListPointer   ; Save host address MC table.
   mov   MCTableOffsetLo, ax
   mov   ax, MulticastListPointer + 2
   mov   MCTableOffsetHi, ax

;---------------------------------------------------------------\
;                                                               *
; Fixup Transmit ECB sequence buffer address.         *
;                                                               *
;---------------------------------------------------------------/

   mov   ax, offset TransmitECB
   shr   ax, 1            ; Adjust for BMIC.
   mov   ReadTCBLocalAddress, ax      ; Store it.

;---------------------------------------------------------------\
;                                                               *
; Allow 586 to initialize itself and jump to main loop.      *
;                                                               *
;---------------------------------------------------------------/

   call   Initialize586
   ;
   ; Fall thru to MainPollingLoop.
   ;

Start   endp

;***********************************************************************\
;                                                                       *
; MainLoop - This is the main polling loop which the processor runs   *
; forever. It will poll the 586 for receives, check the host for the   *
; idle adapter command, check the host for update parameters command   *
; and check the host for another transmit in that order.      *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         (Never returns)         *
;   BX = Not Used                     *
;   CX = Not Used                     *
;   DX = Not Used                     *
;    BP = Not Used                     *
;   SI = Not Used                     *
;   DI = Not Used                     *
;                                                                       *
;***********************************************************************/

   jmp   short MainPollingLoop

IF DEBUG
UpdateDiagBuffer:
   mov   PollLoopCount, 4000h

;   Update diagnostic buffer periodically (approx every 1 sec)

   mov   si, offset DiagnosticSequence   ; SI -> Preset BMIC sequence.
   BMICTransferWithLocal
   jmp   UpdateDiagBufferReturn
ENDIF

MainIdleCommand:
   mov   dx, BMIC_INDEX         ; Set index to the Adapter
   mov   al, IDLE_MAILBOX      ;  Idle mailbox.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   ax, es            ; Clear it.
   out   dx, al

   jmp   IdleAdapter         ; Idle the adapter.

UpdateParmCommand:
   mov   dx, BMIC_INDEX         ; Set index to the Update
   mov   al, PARM_UPDATE_MAILBOX      ;  Parameter mailbox.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   ax, es            ; Clear it.
   out   dx, al

   call   UpdateParameters      ; Update the parameters.
   jmp   short MainPollingLoop

UpdateStatCommand:
   mov   dx, BMIC_INDEX         ; Set index to the Update
   mov   al, STAT_UPDATE_MAILBOX      ;  Parameter mailbox.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   ax, es            ; Clear it.
   out   dx, al

   call   UpdateHostStatistics      ; Update parameters.

   even

MainPollingLoop   proc

   call   Poll586            ; Poll the 82586.

IF DEBUG
   dec   PollLoopCount
   jz   UpdateDiagBuffer
UpdateDiagBufferReturn:
ENDIF
   mov   dx, BMIC_INDEX         ; Set index to the adapter
   mov   al, IDLE_MAILBOX OR AUTO_INC   ;  idle mailbox.
   out   dx, al
   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.

   in   al, dx            ; Read Idle mailbox.
   or   al, al            ; Was command(port 10h) issued?
   jnz   MainIdleCommand         ; Jump if so.

   in   al, dx            ; Read Update Parm mailbox.
   or   al, al            ; Was cmd (port 11h) issued?
   jnz   UpdateParmCommand      ; Jump if so.

   in   al, dx            ; Read Update Stat mailbox.
   or   al, al            ; Was cmd (port 12h) issued?
   jnz   UpdateStatCommand      ; Jump if so.

   in   al, dx            ; Read VALID_TCB_MAILBOX.
   cmp   byte ptr TCBIndex, al      ; Has Tx index caught up to
   jne   TCBReady         ;  tail queue index? If so,
                  ;  then no TCB is waiting.

   in   al, dx            ; Read Polling Status(Port 14h).
   or   al, al
   jz   MainPollingLoop

;   Handle polling timeout

   mov   dx, T2Control
   in   ax, dx
   test   ax, 20h            ; Maximum Count?
   je   MainPollingLoop
   InterruptTheHost
   StatisticsUpdate PollingTimeout      ; Update counters.

   mov   dx, BMIC_INDEX         ; Set index to the adapter
   mov   al, POLLING_MAILBOX      ;  poll mailbox.
   out   dx, al
   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   ax, es            ; AX = 0.
   out   dx, al            ; Clear Poll mailbox.

MainPollingLoopJMP:
   jmp   MainPollingLoop

;***************************************************************\
;                                                               *
; When the host has another TCB for us to send, the host writes   *
; the AdapterTCBList's index to the VALID_TCB_MAILBOX port.   *
;                        *
;***************************************************************/

TCBReady:
   cmp   TxFreeHead, 0         ; Is Tx memory available to
                  ;  copy down next host TCB?
   jz   MainPollingLoop         ; Jump if not.

IF DEBUG
   push   ax
   mov   al, 'T'
   call   OutChar
   mov   ax, TCBIndex
   call   OutWord
   pop   ax
   sub   ah, ah
   call   OutWord
ENDIF

   mov   dx, BMIC_INDEX
   mov   al, PEEK_POKE_ADDR OR AUTO_INC
   out   dx, al
   mov   dx, BMIC_DATA

   mov   bx, TCBListPointer + 2      ; BX -> host TCB ptr List.
   mov   ax, TCBIndex         ; AX = Index into ptr list.
   shl   ax, 2            ; AX = Index * 4.
   add   ax, TCBListPointer      ; Add to base(lo adr) of list.
   adc   bx, 0            ; BX:AX -> Next TCB slot.

   out   dx, al            ; Set addr 0.
   mov   al, ah            ;
   out   dx, al            ; Set addr 1.
   mov   ax, bx            ;
   out   dx, al            ; Set addr 2.
   mov   al, ah            ;
   out   dx, al            ; Set addr 3.
   mov   dx, BMIC_INDEX         ; Set index to Peek/Poke
   mov   al, PEEK_POKE_CONTROL      ;  control register.
   out   dx, al            ;
   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   al, 01011111b         ; Set to peek host memory
   out   dx, al            ;  and start it.

   mov   dx, BMIC_STATUS         ; DX = BMIC Status port.

WaitForTCBPeek:               ;
   in   al, dx            ; AL = current status.
   test   al, PEEK_POKE_PENDING      ; Peek done?
   jnz   WaitForTCBPeek         ; Jump if not.

   mov   dx, BMIC_INDEX         ; Set index to Peek/Poke
   mov   al, PEEK_POKE_DATA OR AUTO_INC   ;  data register.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   di, offset ReadTCBTransferOffsetLo; DI -> Next Empty TCB.
   mov   cx, 4            ; Read 4 bytes of TCB ptr
 rep   insb               ;  from host AdapterTCBList.

IFDEF NE3200P   ; Ne3200Promisc code
   cmp     ReadTCBTransferOffsetHi, -1   ; Have a valid TCB ptr?
   jne     MainPollTxReady         ; Jump if yes.

   mov     dx, BMIC_INDEX                  ; Set index to Valid TCB
   mov     al, VALID_TCB_MAILBOX           ;  mailbox.
   out     dx, al

   mov     dx, BMIC_DATA                   ; DX = BMIC Data port.
   mov     ax, es                          ; Clear Valid TCB Mailbox.
   out     dx, al            ; Attempt to recover from
                  ;  a bad index value.
   jmp     MainPollingLoop
MainPollTxReady:

ENDIF
                  ; Already chk TxFreeHead!=0.
   mov   di, TxFreeHead         ; DI ->FreeList of TxCStruct.
   mov   ax, [di].TxCLink      ; Get ptr to next TxCStruct
   mov   TxFreeHead, ax         ;  and save it as new head.

   jmp   ReadTCBFromHost         ; Copy host TCB to our buffers.

MainPollingLoop   endp


;***************************************************************\
;                                                               *
; Routines for helping to send frames.            *
;                                                               *
;***************************************************************/

Tx8022:
   add   word ptr TransmitECB.ATPacketLength, 3   ; Add 802.2 size.
   mov   ax, word ptr TransmitECB.ATPacketLength   ; AX = Packet Length.
   cmp     ax, 1500
   ja   TxPacketTooBig

   xchg   al, ah            ; Convert to hi/lo format(Endian).
   stosw
   lea   di, [bx+size TxCStruct + size BDStruct]
   cmp   byte ptr TransmitECB.ATProtocolID + 0, 00000010b
   jae   Tx8022Aware

   mov   al, byte ptr TransmitECB.ATProtocolID + 5
   mov   ah, al            ; AX = DSAP, SSAP.
   stosw
   mov   OddByteStorage, 0103h      ; Set odd flag with Ctrl0.

   mov   si, di            ; SI -> Tx Buffer.
   mov   di, offset TransmitECB.ATPacketOffset

   inc   word ptr [di + 4]      ; Add one to fragment size.
   sub   word ptr [di + 0], 1      ; Subtract one from
   sbb   word ptr [di + 2], 0      ;  fragment address.

   or   bp, bp
   je   EverythingDoneJMP
   jmp   CopyFragmentsOverLoop      ; Copy first fragment.


TxPacketTooBig:
   StatisticsUpdate PacketTxTooBigCount    ; Update counters.

   mov     ax, TxFreeHead                  ; AX -> old Free Head.
   mov     TxFreeHead, bx                  ; Free Head = BX.
   mov     [bx].TxCLink, ax                ; Link Old to New.
   jmp     ClearHostMailbox


EverythingDoneJMP:
   jmp   EverythingDone


Tx8022Aware:
   ja   Tx8022Type2

   mov   ax, word ptr TransmitECB.ATProtocolID + 3
   stosw               ; Store DSAP, SSAP.
   mov   al, byte ptr TransmitECB.ATProtocolID + 5
   mov   ah, 01            ; Set odd flag with Ctrl0.
   mov   OddByteStorage, ax

   mov   si, di            ; SI -> Tx Buffer.
   mov   di, offset TransmitECB.ATPacketOffset

   inc   word ptr [di + 4]      ; Add one to fragment size.
   sub   word ptr [di + 0], 1      ; Subtract one from
   sbb   word ptr [di + 2], 0      ;  fragment address.

   or   bp, bp
   je   EverythingDoneJMP
   jmp   CopyFragmentsOverLoop      ; Copy first fragment.


Tx8022Type2:
   inc   word ptr TransmitECB.ATPacketLength   ; Add Ctl1 size.
   mov   ax, word ptr TransmitECB.ATPacketLength   ; AX = Packet Length.
   cmp     ax, 1500
   ja   TxPacketTooBig

   xchg   al, ah            ; Change Endian type.
   mov   [bx].TxLength, ax
   mov   ax, word ptr TransmitECB.ATProtocolID + 2
   stosw               ; Store DSAP, SSAP.
   mov   ax, word ptr TransmitECB.ATProtocolID + 4
   stosw               ; Store Ctrl0, Ctrl1.

   mov   si, di            ; SI -> Tx Buffer.
   mov   di, offset TransmitECB.ATPacketOffset

   or   bp, bp
   je   EverythingDoneJMP
   jmp   CopyFragmentsOverLoop      ; Copy first fragment.

TxSNAP:
   add   word ptr TransmitECB.ATPacketLength, 8   ; Add 802.2 SNAP size.
   mov   ax, word ptr TransmitECB.ATPacketLength   ; AX = Packet Length.
   cmp     ax, 1500
   ja   TxPacketTooBig

SendPacketOK2:
   xchg   al, ah            ; Convert to hi/lo format.
   stosw
   lea   di, [bx + size TxCStruct + size BDStruct]
   mov   si, offset TransmitECB.ATProtocolID + 1
   mov   ax, 0AAAAh         ; Send DSAP/SSAP.
   stosw
   mov   al, 03            ; Send Ctrl (UI frame).
   stosb
   movsb
   movsw
   movsw
   mov   si, di            ; SI -> Tx Buffer.
   mov   di, offset TransmitECB.ATPacketOffset
   or   bp, bp
   je   EverythingDoneJMP
   jmp   CopyFragmentsOverLoop      ; Copy first fragment.

;---------------------------------------------------------------\
;                                                               *
; Misc. code for jumps.                  *
;                                                               *
;---------------------------------------------------------------/


SkipThisFragmentJMP1:
   jmp   SkipThisFragment       ; Go to next fragment.

HandleOddLengthFragments:
   cmp   bp, 1            ; Last fragment?
   je   short SkipThisFragmentJMP1   ; Jump if so.

   mov   cx, bp            ; CX = fragments left.
   mov   bx, [di + 4]         ; BX = Fragment length.
   mov   al, [si + bx - 1]      ; AL = Value of odd byte.
   mov   ah, 1            ; AH = Odd Byte flag.
   mov   OddByteStorage, ax      ; Set odd byte flag.
   dec   word ptr [di + 4]      ; Decrement count.
   mov   bx, di            ; BX -> Descriptor.
   add   bx, 8            ; BX -> Next Descriptor.

LookForZeroLengthLoop:
   cmp   word ptr [bx + 4], 0      ; Next descriptor 0 length?
   je   HandleZeroFragments      ; jump if so.

   inc   word ptr [bx + 4]      ; Adjust for extra byte.
   sub   word ptr [bx + 0], 1      ; Back up the frag pointer.
   sbb   word ptr [bx + 2], 0
   jmp   SkipThisFragment      ; Go on to next descriptor.

HandleZeroFragments:
   add   bx, 8            ; BX -> Next descriptor.
   dec   cx            ; CX = Descriptors left.
   cmp   cx, 1            ; Last descriptor?
   jne   short LookForZeroLengthLoop   ; Jump if not.
   jmp   SkipThisFragment      ; Jump if last one.


Tx8022JMP:
   jmp   Tx8022            ; Jump to 802.2 handler.

TxSNAPJMP:
   jmp   TxSNAP            ; Jump to SNAP handler.

SendTooBig:               ;;dgm - JCP
   jmp   TxPacketTooBig

;***********************************************************************\
;                                                                       *
; ReadTCBFromHost - We have a TCB ready at the host (HSM) and a free    *
; transmit buffer on the adapter. Transfer the TCB to the adapter and   *
; initiate the sending the frame.               *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Destroyed         *
;   BX = Not Used         BX = Destroyed         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Destroyed         *
;   SI = Not Used         SI = Destroyed         *
;   DI = TxFreeHead         DI = Destroyed         *
;                                                                       *
;***********************************************************************/

   even

ReadTCBFromHost      proc

;;   mov   FragLength, 0         ; LON 1/18/96
   mov   si, offset ReadTCBTransferSequence
   BMICTransferWithLocal   ; TransmitECB -> ATransmitBufferStructure
   mov   bx, di            ; BX -> TxCStruct.
   mov   CurrentTCB, di         ; Save it for later.

   mov   bp, word ptr TransmitECB.ATFragmentCount
   and   bp, 001Fh         ; Protect ourselves!
   cmp   word ptr TransmitECB.ATLogicalID, 0ffffh   ; Raw send?
   je   TxRawSizeChk         ; Jmp if so(Check frame size).

   lea   di, [bx].TxDestination      ; DI -> TxC Destination adr.
   lea   si, TransmitECB.ATImmediateAddress
   mov   cx, 3
 rep   movsw               ; Copy destination address.

   mov   cx, word ptr TransmitECB.ATBoardNumber   ; CX = Board Number.
   cmp   cx, BoardNumber8022      ; 802.2 packet?
   je   Tx8022JMP         ; Jump if so.
   cmp   cx, BoardNumberSNAP      ; 802.2 SNAP packet?
   je   TxSNAPJMP         ; Jump if so.

   mov   ax, word ptr TransmitECB.ATPacketLength   ; AX = Packet Length.
   cmp     ax, 1500         ; Info field of frame <=1500?
   ja   SendTooBig         ; Jump if no.

SendPacketOK3:
   inc   ax            ; Roundup and evenize the
   and   al, 0feh         ;  total byte count.
   mov   word ptr TransmitECB.ATPacketLength, ax

   cmp   cx, BoardNumberEII      ; EII packet?
   je   TxEthernetII         ; Jump if so.
   xchg   al, ah            ; Convert to hi/lo format
   stosw               ;     (change Endians).
TxIsARawSend:               ; JCP, 941202.
   lea   si, [bx + size TxCStruct + size BDStruct]
   mov   di, offset TransmitECB.ATPacketOffset
   or   bp, bp
   je   EverythingDone
   jmp   CopyFragmentsOverLoop      ; Copy next fragment.


TxEthernetII:
   mov   ax, word ptr TransmitECB.ATProtocolID + 4
   stosw
   lea   si, [bx + size TxCStruct + size BDStruct]
   mov   di, offset TransmitECB.ATPacketOffset
   or   bp, bp
   je   EverythingDone
CopyFragmentsOverJMP:
   jmp   CopyFragmentsOverLoop      ; Copy next fragment.


TxRawSizeChk:
   mov     cx, word ptr TransmitECB.ATPacketLength
   cmp     cx, 1514                        ; Raw send <= max frame size?
   jbe   TxIsARawSend         ; Jmp if So (Continue)
   jmp   TxPacketTooBig         ; Exit (Error -pkt too large).


HandleOddLengthFragmentsJMP:
   jmp   HandleOddLengthFragments

TxRawSend:
   mov   ax, bx
   add   ax, SIZE TxCStruct + SIZE BDStruct + 14   ; AX -> TxData.
   mov   [bx + size TxCStruct].BufPtr, ax
   lea   si, [bx + SIZE TxCStruct + SIZE BDStruct] ; SI->Dest Adr in frm.
   lea   di, [bx].TxDestination
   movsw
   movsw
   movsw
   add   si, 6            ; SI -> Len/type field.
   movsw               ; Save Len/type into 82586's
                  ;  Transmit Cmd Block (TCB).
   mov   cx, word ptr TransmitECB.ATPacketLength   ; CX = Packet Length.
   sub   cx, 14               ; Subtract header.

   mov   ax, word ptr TransmitECB.ATBoardNumber   ; AX = Board Number.
   cmp   ax, BoardNumber8022      ; 802.2 packet?
   je   TxBufPtrSet         ; Jump if so (continue).
   cmp   ax, BoardNumberSNAP      ; 802.2 SNAP packet?
   je   TxBufPtrSet         ; Jump if so (continue).

;-----------------------------------------------------------------------\
;                           *
; Only evenize RAW frames of type 802.3 and EII (not 802.2 & SNAP).   *
;                           *
;-----------------------------------------------------------------------/

   inc   cx            ; Roundup and evenize the
   and   cl, 0feh         ;  total byte count.
   mov   word ptr TransmitECB.ATPacketLength, cx ; Save adjusted size.
   jmp   short TxBufPtrSet      ; Continue.



ProcessNextDescriptor:
   test   word ptr [di + 4], 1      ; Fragment odd?
   jnz   HandleOddLengthFragmentsJMP   ; Jump if so.

;-----------------------------------------------------------------------\
;                           *
; For the below code: [di+4] = a pointer to ATPacketSize;      *
;       8 = the byte size of ATPacketOffset and ATPacketSize fields;   *
;                           *
;-----------------------------------------------------------------------/

SkipThisFragment:
   add   si, [di + 4]         ; SI -> End of Tx Buffer.
   add   di, 8            ; DI -> Next Descriptor.
   dec   bp            ; BP = New Descriptor count.
   jnz   CopyFragmentsOverJMP      ; Exit if no more descriptors.

;---------------------------------------------------------------\
;                                                               *
; All fragments have been transfered. Prepare Transmit buffer   *
; for transmission by the 82586.            *
;                                                               *
;---------------------------------------------------------------/

EverythingDone:
   mov   OddByteStorage, es      ; Clear (zero) odd byte flag.
   mov   bx, CurrentTCB         ; BX -> TCB.
   cmp   word ptr TransmitECB.ATLogicalID, 0ffffh  ; RAW 802.3 send?
   je   TxRawSend              ; Jump if so.

   mov   ax, bx
   add   ax, SIZE TxCStruct + SIZE BDStruct   ; AX -> Tx Data.
   mov   [bx + size TxCStruct].BufPtr, ax
   mov   cx, word ptr TransmitECB.ATPacketLength   ; CX = Packet Length.

TxBufPtrSet:
   cmp   cx, 60-14         ; Over Minimum Size?
   ja   TxLengthOK         ; Jump if so.

   mov   cx, 60-14         ; Force to Minimum size.

TxLengthOK:
   or   cx, 8000h         ; Set TBD EL(End of List)bit.
   mov   [bx + size TxCStruct].BDStatus, cx
   mov   [bx].TxCLink, 0         ; No next Transmit Cmd Blk(TCB).
   mov   [bx].TxTries, MAX_TX_RETRIES   ; Set Retry count.

   mov   si, TxCEnd         ; Is Tx ptr list empty?
   or   si, si
   jne   AddToList         ; Jump if not.

   mov   TxCHead, bx         ; Initialize Tx ptr List.
   mov   TxCEnd, bx
   mov   DeadManHigh, 5         ; Set Tx Deadman.
   mov   ax, 100h         ; SCB 100 = start exec of CBL.

   mov   cx, es            ; CX = 0.

WaitFor586Loop:
   cmp   ds:SCB.SCBCommand, 0      ; Status complete?
   loopne   WaitFor586Loop         ; Loop if not.
   jne   ReStart586FromTx      ; Jump if never complete.

   mov   ds:SCB.SCBCommand, ax      ; Command 586.
   mov   ds:SCB.CBLOffset, bx      ; Point to First packet.
   mov   dx, CA_PORT         ; Start the 586.
   out   dx, al

ClearHostMailbox:
   mov   dx, BMIC_INDEX         ; Set index to Peek/Poke
   mov   al, PEEK_POKE_DATA OR AUTO_INC   ;  data register.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   al, -1            ; AL = -1.
   out   dx, al            ; A -1 tells the host
   out   dx, al            ;  that we've copied its
   out   dx, al            ;  RCB and the host can now
   out   dx, al            ;  return the RCB to the OS.

   mov   bx, TCBListPointer + 2      ; BX -> host TCB List.
   mov   ax, TCBIndex         ; AX = Index into list.
   shl   ax, 2            ; AX = Index * 4.
   add   ax, TCBListPointer      ; Add to base of list.
   adc   bx, 0            ; BX:AX -> Next TCB slot.

   out   dx, al            ; Set addr 0.
   mov   al, ah
   out   dx, al            ; Set addr 1.

   mov   ax, bx
   out   dx, al            ; Set addr 2.
   mov   al, ah
   out   dx, al            ; Set addr 3.

;---------------------------------------------------------------\
;                                                               *
; Indicate to the host that we are done processing an RCB by   *
; writing a -1 into its adapterTCBList pointer table. The host   *
; can now return the RCB (ECB) back to the the OS for re-use.   *
;                        *
;---------------------------------------------------------------/

   mov   dx, BMIC_INDEX         ; Set index to Peek/Poke
   mov   al, PEEK_POKE_CONTROL      ;  control register.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   al, 00111111b         ; Set poke at host memory
   out   dx, al            ;  and start it.

   mov   dx, BMIC_STATUS         ; DX = BMIC Status port.

WaitForTCBPoke:
   in   al, dx            ; AL = current status.
   test   al, PEEK_POKE_PENDING      ; Locked exchange done?
   jnz   WaitForTCBPoke         ; Jump if not.

   inc   byte ptr TCBIndex
   cmp   PollTimeout, 0         ;#
   jne   MainPollingLoopJMP2      ;#

   InterruptTheHost

MainPollingLoopJMP2:            ;#
   jmp   MainPollingLoop

AddToList:
   mov   [si].TxCLink, bx      ; Link to end of list.
   mov   TxCEnd, bx
   jmp   short ClearHostMailbox

ReStart586FromTx:
   call   ReStart586         ; 586 might have hung.
   jmp   MainPollingLoop

SkipThisFragmentJMP:
   jmp   SkipThisFragment      ; Go on to next fragment.

AdjustFragmentForMin:
   mov   cx, 6            ; Force to minimum size.
   jmp   short FragmentAdjustedForMin

;;FragLengthError:            ; LON 1/18/96
;;   StatisticsUpdate BadFragLengthCount   ; Update counters.
;;   jmp   ClearHostMailbox

;***************************************************************\
;                                                               *
; Copy the next fragment over from the driver to us (firmware).   *
;                                                               *
; SI -> Area in Tx Buffer to continue copying into.      *
; DI -> Descriptor of next fragment.            *
; BP =  Descriptors left (counting this one).         *
;                                                               *
;***************************************************************/

CopyFragmentsOverLoop:            ; [di+4] -> ATPacketSize
   mov   cx, [di + 4]         ; CX = Fragment length.
   jcxz   SkipThisFragmentJMP      ; Skip if its zero.

   cmp   cx, 6            ; Large enough for BMIC?
   jb   short AdjustFragmentForMin   ; Jump if not.

FragmentAdjustedForMin:

;; This check was done previously
;;
;;   add   FragLength, cx
;;   cmp   FragLength, 1514      ; Over Max?
;;   ja   FragLengthError         ; Jump if so.

   mov   dx, BMIC_INDEX         ; Set index to channel 1
   mov   al, CH1_TBI_BASE_ADDR OR AUTO_INC ; TBI Base Address register.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   ax, si            ; AX -> local address.
   shr   ax, 1            ; Adjust for BMIC.
   out   dx, al            ; Set LSB.
   mov   al, ah
   out   dx, al            ; Set MSB.

   mov   dx, BMIC_INDEX         ; Set index to channel 1.
   mov   al, CH1_BASE_COUNT OR AUTO_INC   ; base count register(3 bytes)
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   ax, cx            ; AX = Fragment length.
   out   dx, al            ; Send LSB of count.
   mov   al, ah
   out   dx, al            ; Send MSB of count.

   mov   al, AUTO_START         ; Set Auto start, Host to
   out   dx, al            ;  adapter byte.

;---------------------------------------------------------------\
;                        *
; Setup the 4 byte CH1_BASE_ADDR port register         *
;                        *
;---------------------------------------------------------------/
   mov   ax, [di + 0]         ; AX = Host adr low.
   out   dx, al            ; AX = ATPacketOffset low.
   mov   al, ah
   out   dx, al
   mov   ax, [di + 2]         ; AX = Host adr hi
   out   dx, al            ;  (AX =ATPacketOffset high).
   mov   al, ah
   out   dx, al

   mov   dx, BMIC_INDEX         ; Set index to channel 1
   mov   al, CH1_STROBE         ; strobe register.
   out   dx, al
   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   out   dx, al            ; Strobe channel 1.

   mov   dx, BMIC_INDEX         ; Set index to channel 1
   mov   al, CH1_STATUS         ; status register.
   out   dx, al            ;
   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.

WaitForFragmentTransfer:         ;
   in   al, dx            ; AL = current status.
   test   al, TRANSFER_ENABLED OR TRANSFER_IN_PROGRESS
   jnz   short WaitForFragmentTransfer
   and   al, 00000011b         ; MaskOut reserve bits(sticky)
   out   dx, al            ; Clear the completion bit.

   cmp   OddByteStorage, 0      ; Last fragment odd?
   jnz   OddByteFix         ; Jump if so.
   jmp   ProcessNextDescriptor      ; Done if not.

OddByteFix:
   mov   cx, OddByteStorage      ; CL = odd byte value.
   mov   [si], cl         ; Store it into buffer.
   mov   OddByteStorage, es      ; Clear (zero) odd byte flag.
   jmp   ProcessNextDescriptor      ; Done with this fragment.

ReadTCBFromHost      endp


;***************************************************************\
;                                                               *
; Misc. routines for handling exceptions in a received frame.   *
;                                                               *
;***************************************************************/


GetADefaultBoardNumber:
   or   byte ptr [bx].ARESREBXValue, 20h ; Error - Set No Registered
   mov   ax, BoardNumber8023       ;  Board error status.
   cmp   ax, -1
   jne   FoundADefaultBoardNumber
   mov   ax, BoardNumberEII
   cmp   ax, -1
   jne   FoundADefaultBoardNumber
   mov   ax, BoardNumber8022
   cmp   ax, -1
   jne   FoundADefaultBoardNumber
   mov   ax, BoardNumberSNAP
FoundADefaultBoardNumber:

IFNDEF NE3200P
    ret               ; Exit.

CheckForDeformedPacket:

   cmp     bp, 14            ; Big enough for 802.3 hdr?
   jb      CheckNotDeformed      ; Can't get type if not.

   mov     cx, [si].UFDLength      ; CX = Type/Length field.
   xchg    cl, ch                          ; Convert to Intel.
   cmp     cx, 1500                        ; EII Packet?
   ja      CheckNotDeformed      ; Jump if so.

   add     cx, 14                          ; Add 802.3 size.
   cmp     cx, bp            ; Header len > bytes read in?
   jbe     CheckDeformTooSmall      ; Jump if not.
   or      ax, 400h         ; Set ASPEC Malform err status
;;   or      byte ptr [bx].ARESREBXValue, 40h  ; Set CSPEC Malform err Status.


CheckDeformTooSmall:
   cmp     bp, 22            ; Valid 802.2 header?
   jb      CheckNotDeformed      ; Jump if not.

   mov     dx, 14                          ; DX = Assume 802.3 size.
   cmp   word ptr [si].DataBuffer, 0ffffh  ; RAW 802.3? (Byte 14 of frm).
   je      CheckDeformHeader      ; Jump if so.

   mov     dx, 22                          ; DX = Assume Snap size.
   cmp   word ptr [si].DataBuffer, 0aaaah ; SNAP?  (Byte 14 of frm)

   je      CheckDeformHeader      ; Jump if SNAP.

   mov     dx, 18                          ; DX = Assume 802.2 Type 2 size.
   test    byte ptr [si].DataBuffer +2, 00000001b ; I-Format PDU?
   je      CheckDeformHeader      ; Jump if so.

   test    byte ptr [si].DataBuffer +2, 00000010b ; S-Format PDU?
   je      CheckDeformHeader      ; Jump if so.

   dec     dx            ; DX = 17 = 802.2 Type 1 size.

CheckDeformHeader:
   sub     cx, dx                          ; Enough for valid header?
   jns     CheckNotDeformed      ; Jump if so.

   or      ax, 400h         ; Set ASPEC Malform error Status.
;;   or      byte ptr [bx].ARESREBXValue, 40h ; Set CSPEC Malform error Status.

CheckNotDeformed:
   ret               ; Exit.


ELSE   ; Ne3200Promisc. code

CheckForDeformedPacket:
   cmp     RBDTotalSize, 14                ; Big enough for RAW 802.3 hdr?
   jb      CheckNotDeformed      ; Can't get type if not.

   mov     cx, [bx +size AReceiveBufferStructure + 12] ; CX = Type/Length field.
   xchg    cl, ch                          ; Convert to Intel.
   cmp     cx, 1500                        ; EII Packet?
   ja      CheckNotDeformed      ; Jump if so.

   add     cx, 14                          ; Add 802.3 size.
   cmp     cx, RBDTotalSize                ; Header len > bytes read in?
   jbe     CheckDeformTooSmall      ; Jump if not.

   or      byte ptr [bx].ARESREBXValue+1, 4 ; Set ASPEC Malform error Status.
;;   or      byte ptr [bx].ARESREBXValue, 40h ; Set CSPEC Malform error Status.

CheckDeformTooSmall:
   cmp     RBDTotalSize, 22                ; Valid 802.2 header?
   jb      CheckNotDeformed      ; Jump if not.

   mov     dx, 14                          ; DX = 802.3 size.
   mov     si, SaveRDBOffset
   mov     si, [si].RBDBufPtr              ; SI -> Data.
   cmp     word ptr [si], 0ffffh           ; RAW 802.3?
   je      CheckDeformHeader      ; Jump if so.

   mov     dx, 22                          ; DX = Snap size.
   cmp     word ptr [si], 0aaaah           ; SNAP?
   je      CheckDeformHeader      ; Jump if SNAP.

   mov     dx, 18                          ; DX = 802.2 Type 2 size.
   test    byte ptr [si+2], 00000001b      ; I-Format PDU?
   je      CheckDeformHeader      ; Jump if so.

   test    byte ptr [si+2], 00000010b      ; S-Format PDU?
   je      CheckDeformHeader      ; Jump if so.

   mov     dx, 17                          ; DX = 802.2 Type 1 size.

CheckDeformHeader:
   sub     cx, dx                          ; Enough for valid header?
   jns     CheckNotDeformed      ; Jump if so.

   or      byte ptr [bx].ARESREBXValue+1, 4 ; Set ASPEC Malform error Status.
;;   or      byte ptr [bx].ARESREBXValue, 40h ; Set CSPEC Malform error Status.
CheckNotDeformed:
   ret               ; Exit - return.

;------------------------------------------------------------------------*
;------------------------------------------------------------------------*

SetDestTypeError:            ; Only this bit can be set.
   mov   byte ptr [bx].ARDriverWorkSpace, ECB_ERR_PKT ; Pkt has err(s).
   jmp   DestTypeErrReturn      ; Continue processing.

ENDIF   ; End Ne3200Promisc.


RxMalformedInPromisc:
   mov   ax, bp              ; Restore to Total frm size.
   or   byte ptr [bx].ARESREBXValue+1, 4  ; Set ASPEC Malform Err Status.
;;   or   byte ptr [bx].ARESREBXValue, 40h  ; Set CSPEC Malform Err Status.
   jmp   RxMalformedPromiscReturn     ; Continue processing.

RxMalformedPacket:
   StatisticsUpdate  HardwareRxMismatchCount
   cmp   PromiscuousMode, 0   ; In Promiscuous Mode?
   jne   RxMalformedInPromisc   ; Jmp if so (Keep the frame).
   ret            ; Ret FRInterrupt (Ignore bad frame).

RxMalformedTooSmall:
   StatisticsUpdate  HardwareRxMismatchCount
   cmp     PromiscuousMode, 0
   jne     RxMalformedTooSmallInPromisc
   ret            ; Return to FRInterrupt.


RxMalformedTooSmallInPromisc:
IFNDEF NE3200P
   mov     ax, bp            ; Restore the total frm size.
ELSE
   mov     ax, RBDTotalSize      ; Restore the total frm size.
ENDIF
   mov     dx, es            ; DX= 0 (Offset to frm start).
   or      byte ptr [bx].ARESREBXValue+1, 4  ; Set ASPEC Malform Error Status.
;;   or      byte ptr [bx].ARESREBXValue, 40h  ; Set CSPEC Malform Error Status.
                  ; Fall thru.
SetGlobalError:
   mov   byte ptr [bx].ARDriverWorkSpace, ECB_ERR_PKT ; Pkt has err(s).

IFNDEF NE3200P
   cmp     bp, 22                   ; Frame big enough for largest
   jae   SetGlobalErrorExit   ;  possible MediaHdr? Jmp if Yes.
   mov   dx, es         ; Don't guess hdr size so setup DX
               ;  with offset to frame start.
   mov   ax, bp         ; AX = Total frm size (Includes media).
   cmp   bp, cx         ; Is Media Size larger than pkt?
   jns   SetGlobalErrorExit   ; Jmp if no.
   mov   cx, bp         ; Set Media Size = Frame Size.
SetGlobalErrorExit:

ENDIF
   jmp   RxContinue      ; Continue processing.


RxCheckMulticastJMP:
   mov   byte ptr [bx].ARDriverWorkSpace, ECB_BROADCAST OR ECB_MULTICAST
   mov   ax, word ptr [bx+size AReceiveBufferStructure+0]
   and   ax, word ptr [bx+size AReceiveBufferStructure+2]
   and   ax, word ptr [bx+size AReceiveBufferStructure+4]
   cmp   ax, 0ffffh         ; Broadcast?
   je   RxBackFromMulticastCheck   ; Jump if So (Continue processing).

;-----------------------------------------------------------------------\
;                                                                  *
; Scan multicast table for a match with the destination address.   *
;                                                                  *
;-----------------------------------------------------------------------/

RxCheckMulticastTable:
   mov   byte ptr [bx].ARDriverWorkSpace, ECB_MULTICAST   ; Maybe multicast.
   mov   cx, MulticastCount      ; CX = addresses in table.
   jcxz   RxNoMulticastMatch      ; Exit if no addresses.

   mov   di, offset HostMulticastTable   ; DI -> MC table.

;-----------------------------------------------------------------------\
;                           *
; For Non-P Ne3200 code, [bx+size AReceiveBufferStructure+0] points   *
; to the UniReceiveBuf.UHeader field (for Ne3200Promisc its RCVMACDest).*
;                           *
;-----------------------------------------------------------------------/
   mov   ax, word ptr [bx+size AReceiveBufferStructure+0] ; AX = 1st word.
   mov   dx, word ptr [bx+size AReceiveBufferStructure+2] ; DX = 2nd word.
   mov   si, word ptr [bx+size AReceiveBufferStructure+4] ; SI = 3rd word.

CheckMulticastLoop:
   cmp   word ptr [di+0], ax         ; First word match?
   jne   CheckNextMulticast      ; Jump if not.
   cmp   word ptr [di+2], dx         ; Second word match?
   jne   CheckNextMulticast      ; Jump if not.
   cmp   word ptr [di+4], si         ; Third word match?
   je RxBackFromMulticastCheck      ; Jmp if Yes (Continue processing).

;;   jne   CheckNextMulticast      ; Jump if not.

CheckMulticastFound:
;;   jmp   RxBackFromMulticastCheck   ; Success! Continue processing.

CheckNextMulticast:
   add   di, 8            ; DI -> Next table address.
   loop   CheckMulticastLoop      ; Try again.

RxNoMulticastMatch:
   mov   byte ptr [bx].ARDriverWorkSpace, ECB_REMOTE_MULTICAST  ; Maybe multicast.
   cmp   PromiscuousMode, 0      ; In promiscuous mode?
   jne   RxBackFromMulticastCheck   ; Jmp if Yes (Continue processing).
   ret               ; Ignore frm-Return to FRInterrupt.


RxMalformedPacketJMP:
   jmp   RxMalformedPacket


Rx8023InPromisc:
   call   GetADefaultBoardNumber
   jmp   short Rx8023NotSupportedPromReturn  ; Continue processing.


Rx8023NotSupported:
   cmp   PromiscuousMode, 0      ; In Promiscuous Mode?
   jne   Rx8023InPromisc         ; Jmp if yes.
   ret               ; Return to FRInterrupt.


RxRemoteUnicast:
   mov   byte ptr [bx].ARDriverWorkSpace, ECB_REMOTE_UNICAST ; Maybe remote.
   jmp   short RxBackFromMulticastCheck


RxMalformedTooSmallJMP:
   jmp   RxMalformedTooSmall

RxEthernetIIJMP:
   jmp   RxEthernetII         ; Jump to Ethernet II handler.

Rx8022JMP:
   jmp   Rx8022            ; Jump to 802.2 handler.

IFNDEF NE3200P
SetGlobalErrorJMP:
   jmp   short SetGlobalError      ; Set err in destination type.

ENDIF

;***********************************************************************\
;                                                                       *
; Copy Packet To Host - At this point, a valid packet has been received *
; and the host has an ECB (RCB) available for us to copy it into.   *
;                           *
; Previously FRInterrupt has already copied the frame header (14 bytes)   *
; into the UHeader (or RCVMACxxxx) field. This procedure fills in   *
; the remainder of the RCB template (AReceiveBufferStructure).      *
; Next the template and the frame are copied to the host (driver). The   *
; template is copied into the driver's ECB starting at the RProtocolId   *
; field, by adding an offset of 16 to the starting address of the ECB.   *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used            AX = Destroyed      *
;   BX = @ UniReceiveBuf.URCB (RCBtemplate)   BX = Destroyed      *
;          or @ of RCB_TEMPLATE (Ne3200Promisc).            *
;   CX = Not Used            CX = Destroyed      *
;   DX = Not Used            DX = Destroyed      *
;    BP = Frame Length (Includes header)   BP = Destroyed      *
;   SI = Not Used            SI = Destroyed      *
;   DI = Not used            DI = Destroyed      *
;                                                                       *
;***********************************************************************/
   even

CopyPacketToHost   proc

;;   mov   bp, cx            ; BP = Packet size.

IFNDEF NE3200P

;-----------------------------------------------------------------------\
;                           *
; During frame reception the 82586 copies the frame header into the   *
; Frame Descriptor (FD) starting at UniReceiveBuf.UFDDestination. The   *
; driver (HSM) requires the header to precede frame data (info field).   *
; Therefore we copy the header ahead of the frame's info field.      *
;                           *
;-----------------------------------------------------------------------/

;;;;;   ;;LON   lea   di, [si + 14 + size AReceiveBufferStructure]
;;   lea   di, [si].UHeader      ;
;;   lea   si, [si].UFDDestination      ; SI -> Media header in FDesc.
;;   mov   cx, 14 / 2         ; Copy 14 bytes of frame hdr.
;; rep   movsw               ; Movsw increments SI to point
                  ;  to UniReceiveBuf.URCB.
;;lea    bx, [si].URCB         ; BX -> RCB structure template.

ELSE   ; Ne3200Promisc code
;;   mov     di, [si].UFDRBDOffset           ; DI -> First RBD.
;;   mov     SaveRDBOffset, di               ; Save ptr to first RBD.

;;lea     bx, RCB_TEMPLATE      ; BX -> RCB structure template.

;;   lea     si, [si].UFDDestination      ;
;;   lea     di, [bx+size AReceiveBufferStructure] ; DI -> RCVMACDest.
;;   mov     cx, 14 / 2                      ; Copy 14 bytes of hdr (DA/SA/len).
;; rep   movsw

ENDIF

;-----------------------------------------------------------------------\
;                           *
; On the host (see DriverISR procedure), ARESREBXValue is the first   *
; dword of RProtocolWorkspace where the frame's error status is saved.   *
; The second dword of RProtocolWorkspace is the frame's byte length.   *
;                           *
;-----------------------------------------------------------------------/
;;   xor   ah, ah                           ; Setup Status & Size in template.
;;;;;
;   mov   word ptr [bx].ARESREBXValue, ax  ; RProtocolWorkspace's 1st
;   mov   word ptr [bx].ARESREBXValue+2, 0 ;  dword (error status).
;   mov   [bx].ARSocket, bp       ; RProtocolWorkspace's 2nd
;   mov   [bx].ARProtocolWorkspaceRest, 0    ;  dword (frame size).
;;;;;   cmp   bp, 1514
;;   ja   PacketTooLong          ; Jump if frame too large.

PacketTooLongReturn:
   test   byte ptr [bx+size AReceiveBufferStructure], 01 ; Multicast?
   jnz   RxCheckMulticastJMP                ; Jump if so.

   mov   byte ptr [bx].ARDriverWorkSpace, ECB_DIRECT ; Must be direct unicast.
   lea   si, [bx+size AReceiveBufferStructure] ; SI->UniReceiveBuf.UHeader
                        ;  or RCVMACDest field.
   lea   di, BoardID         ; DI -> Our IA (Node address).
   mov   cx, 3
 repe   cmpsw               ; Our IA = Destination Adr?
   jne   RxRemoteUnicast         ; Jump if No.

RxBackFromMulticastCheck:

   lea   di, [bx].ARProtocolID      ; DI ->RCB template ProtocolID.

;-----------------------------------------------------------------------\
;                           *
; For Non-P Ne3200 code, [bx+ size AReceiveBufferStructure +12] points   *
;     inside the UniReceiveBuf.UHeader field to the frame type/len.   *
; For Ne3200Promisc, [bx+....] points to the RCVMACLength field.   *
;                           *
;-----------------------------------------------------------------------/
   mov   ax, [bx+size AReceiveBufferStructure + 12] ; 12 = DA + SA size.
   xchg   al, ah            ; AX = Ethernet length/type.
   cmp   ax, 1500         ; Is this EthernetII type?
   ja   RxEthernetIIJMP         ; Jump if So.

;971114 LON Begin
;;JCJ: Start  Check whether Frame length is same as packet length
;;      add     ax, 14                          ; add media header size
;;      cmp     ax, 60                          ; Is it padded
;;      jbe     CheckIfPadded
;;
;;      cmp     ax, bp                          ; Is length over the real size
;;                                              ;  given by 586 ActualCount?
;;      jne     RxMalformedPacketJMP            ; Jmp if So
;;      jmp     short RxNotMalformed
;;CheckIfPadded:
;;      cmp     bp, 60                          ; Was it padded to Min. size
;;      jne     RxMalformedPacketJMP
;;
;;RxNotMalformed:
;;       mov     bp, ax                          ; BP = Unpadded length
;;
;;;JCJ: End Check whether Frame length is same as packet length. SPD# 168702

        cmp     ax, bp                          ; Is length over the real size
                                                ;  given by 586 ActualCount?
        ja      RxMalformedPacketJMP            ; Jmp if So (Chk for PromMode).

        or      ax, ax                          ; Is the length Zero?
        je      RxMalformedPacketJMP            ; Jmp if So (Chk for PromMode).

        mov     bp, ax                          ; BP = Unpadded length plus
        add     bp, 14                          ;  the 802.3 header size.
;971114 LON End


RxMalformedPromiscReturn:

;-----------------------------------------------------------------------\
;                           *
; At this point we know we have a Length field. If the next word after   *
; the length is 0xFFFF then the frame is a Raw 802.3 frame (a Novell   *
; defined frame). This means we encountered an IPX header (i.e. 0xFFFF)   *
; instead of the usual 802.2 DSAP and SSAP header.         *
;                           *
;-----------------------------------------------------------------------/

IFNDEF NE3200P
   cmp   word ptr [bx+size AReceiveBufferStructure + 14], 0ffffh ; Raw 802.3?
   jne   Rx8022JMP         ; Jump if No (Frame is 802.2).
ELSE
   mov     si, SaveRDBOffset      ; Get ptr to 1st RCB.
   mov     si, [si].RBDBufPtr              ; SI -> First Data buffer.
   cmp     word ptr [si], 0ffffh      ; RAW 802.3
   jne     Rx8022JMP                       ; Jump if No (Frame is 802.2).
ENDIF

RxRaw8023:               ; Frame is a RAW 802.3 (IPX)
   mov   ax, es            ; AX = 0 (ES is always zero).
   stosw               ; Clear ARProtocolID in
   stosw               ;  RCB template field.
   stosw               ;  (AX stored into ES[DI] ).

   mov   ax, BoardNumber8023      ; AX = 802.3 board number.
   cmp   ax, -1            ; Valid board number?
   jz   Rx8023NotSupported      ; Jmp if No (Chk for PromMode).

Rx8023NotSupportedPromReturn:
   stosw               ; Save Board # in RCB template.
   mov   ax, es            ; AX = 0.
   stosw               ; Store into RCB template.

   mov   dx, size ATransmitBufferStructure + 14   ; DX = Offset to 1st
                       ;  data byte in ECB buffer.

;-----------------------------------------------------------------------\
;                           *
; At label RxBackFromOtherMedia we return from RxEthernetII and Rx8022   *
; where various header sizes (LLC, SNAP, etc.) were taken into account   *
; in precalculating the host ECB offset (DX) to the 1st frame data byte.*
;                           *
; Note: DI -> ARImmediateAddress in RCB template         *
;                           *
;-----------------------------------------------------------------------/

RxBackFromOtherMedia:
   lea   si, [bx+size AReceiveBufferStructure + 6] ; SI->Source Adr(SA).
   mov   cx, 3            ; Move 3 words of SA into RCB
 rep   movsw               ;  template ARImmediateAddress.

   mov   cx, dx            ; CX= Media Size -includes the
   sub   cx, size ATransmitBufferStructure;  DA, SA, Length/Type and
                  ;   any 802.2 LLC or SNAP size.

IFNDEF NE3200P
   mov     ax, bp                          ; AX = Total Pkt size (Frame
   sub     ax, cx                          ;  size minus the media size).
   js   RxMalformedTooSmallJMP      ; Jmp if frame is just a bad header.

RxCheckGlobalError:
   cmp   word ptr [bx].ARESREBXValue, 0   ; Does frame have any errors?
   jne    SetGlobalErrorJMP      ; Jump if so.
;JCJ 22-October-97 SPD# 168595  If packet size is lesser than 60 bytes that has
;                               to be padded in promiscuous mode.
        cmp   PromiscuousMode, 0      ; In Promiscuous Mode?
        jz   short RxContinue                ; Jmp if no.
   mov   ax, RcvFrameSize      ; Promisc needs the frame data
        sub     ax, cx                          ; and padding but no header.
;JCJ SPD# 168595 End

ELSE   ; Ne3200Promisc code         ; (SNAP hdr = 22).

   cmp     RBDTotalSize, 22                ; Frame big enough for largest
   jae     RxCalcPacketSize                ; possible MediaHdr? Jmp if so.
   mov     dx, es                          ; Don't guess hdr size so setup
                  ;  DX with Offset to frm start.

   mov   ax, bp            ; AX = Total Frm Size (includes media).
   cmp   bp, cx            ; Is Media Size larger than Pkt?
   jns   RxMalformedTooSmallPromisc   ; Jump if No.
   mov   cx, bp            ; Set Media Size = Frame Size.

   jmp   short RxMalformedTooSmallPromisc

RxCalcPacketSize:
   mov     ax, bp                          ; AX = Total Pkt size (Frame
   sub     ax, cx                          ;  size minus the media size).

   jns     RxMalformedTooSmallPromisc   ; Jump if frm OK (It has data).
   jmp     RxMalformedTooSmall      ; Frm has just a hdr so setup
                  ;  DX with Offset to frm start.

RxMalformedTooSmallPromisc :      ; At this label DX = 0 for bad frames.

   cmp   word ptr [bx].ARESREBXValue, 0   ; Does frame have any errors?

;JCJ 22-October-97 SPD# 168595  If packet size is lesser than 60 bytes that has
;                               to be padded in promiscuous mode.
   jz   CheckIfPaddingNeeded      ; Jump if No (Frame is OK).

   jmp   SetDestTypeError      ; Change Dest type to Err Pkt.

CheckIfPaddingNeeded:
        cmp   PromiscuousMode, 0      ; In Promiscuous Mode?
        jz   short RxContinue                ; Jmp if no.
   mov   ax, RBDTotalSize      ; Promisc needs the frame data
        sub     ax, cx                          ;   and padding but no header.
;JCJ SPD# 168595  End

DestTypeErrReturn:
ENDIF   ; End Ne3200Promisc.

RxContinue:
   lea   di, [bx].ARPacketLength      ; DI->Total Length minus media.
   stosw               ; Save length(AX) in RCB template.
;; 980504 LON Begin
   lea   di, [bx].ARPacketSize           ; DI->Frag Size minus media.
   stosw               ; Save length(AX) in RCB template.
                       ; Hi wrd of size already zeroed.
;; LON End

IF DEBUG
   call   OutWord            ; Get packet length-header.
ENDIF
   lea   di, [bx].ARPacketOffset      ; DI-> Template's PacketOffset.

   mov   ax, NextEmptyRCB      ; AX->Lo host ECB address plus
   add   ax, dx            ;  the offset within host ECB
                  ;  to the 1st frame data byte.
   stosw               ; Save Lo ptr to 1st data byte.
   mov   ax, NextEmptyRCB + 2      ; AX -> Hi host ECB address.
   adc   ax, 0
   stosw               ; Save Hi ptr into RCB template.
;;LON Begin 980504
;;   mov   ax, bp            ; AX = Size of only Fragment
;;   sub   ax, cx            ;  minus the media size.
;;   stosw               ; Save PacketSize in RCB template.
;;LON End                ; Hi wrd of size already zeroed.

IFNDEF  NE3200P

if SupportJabber
;;;;;
;   mov   si, FDHead         ; SI -> Current Frame Desc.
;   mov   si, [si].URBDOffset
;   mov   cx, [si+0]         ; CX = EOF/F/ActualCnt of RBD.
;   and   ch, 3Fh            ; Mask off EOF & F bits.
;;;;;
   mov   cx, RcvFrameSize      ; Get the total frame size.

   add   cx, size AReceiveBufferStructure  ; Add RCB template size
                  ; CX=Total bytes for BMIC copy.
   mov   si, bx            ; SI -> RCB template.
else
   mov   si, bx            ; SI -> RCB template.
   mov   cx, bp            ; CX = Packet Size.
   add   cx, size AReceiveBufferStructure; CX=Total bytes for BMIC copy.
endif
ELSE
   mov   si, bx            ; SI -> RCB template.
   mov     cx, TEMPLATE_MAC_SIZE      ; RCB template size + MAC size.
                  ; CX=Total bytes for BMIC copy.
ENDIF
   mov   di, NextEmptyRCB      ; DI = Host ECB Address Lo.
   mov   bx, NextEmptyRCB + 2      ; BX = Host ECB Address Hi.
   add   di, 16            ; DI-> Into ECB (RProtocolID).
   adc   bx, 0            ; Adjust Hi address.

;------------------------------------------------------------------------\
;                            *
; Setup BMIC regs to copy the template plus all of the receive frame    *
;   to the host (HSM)  (Unless we are running as an Ne3200Promisc in    *
;   which case the template and the 14 bytes of the frame header ( DA,    *
;   SA & len) are first copied then another copy does the remainder).    *
;                            *
;   1. CH1_TBI_BASE_ADDR Reg: port 6B <- source low byte local adr    *
;              port 6C <- source hi byte local adr    *
;   2. CH1_BASE_COUNT Reg:     port 60 <- Low count of bytes to xfer    *
;              port 61 <- Hi byte count       *
;   3. CH1_BASE_ADDR Reg:     port 63 & 64 <- dest low word host adr *
;                   port 65 & 66 <- dest. hi word host adr *
;   4. CH1_STROBE Reg:     port 69 <-write anything to start xfer *
;                            *
;------------------------------------------------------------------------/

   mov   dx, BMIC_INDEX         ; Set index to channel 1
   mov   al, CH1_TBI_BASE_ADDR OR AUTO_INC ; TBI base address register.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   ax, si            ; AX = Local addr of RCB template.
   shr   ax, 1            ; Adjust for the BMIC.
   out   dx, al            ; Set LSB of local addr.
   mov   al, ah
   out   dx, al            ; Set MSB of local addr.

   mov   dx, BMIC_INDEX         ; Set index to channel 1
   mov   al, CH1_BASE_COUNT OR AUTO_INC   ;  base count register.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   ax, cx            ; AX = Packet Size.

;;;;;
;;IFNDEF NE3200P   ; Non-P Ne3200 LON
;;if SupportJabber
;;   add   ax, size AReceiveBufferStructure
;;endif
;;ENDIF
   out   dx, al            ; Set lo count in CH1_BASE_COUNT.
   mov   al, ah
   out   dx, al            ; Set hi count.

   mov   al, AUTO_START OR ADAPTER_TO_EISA
   out   dx, al

   mov   ax, di            ; AX = host addr lo.
   out   dx, al
   mov   al, ah
   out   dx, al

   mov   ax, bx            ; AX = host addr hi.
   out   dx, al
   mov   al, ah
   out   dx, al

;---------------------------------------------------------------\
;                                                               *
; Tell BMIC to start the copy of the RCB template plus the:   *
;     1. entire rcv frame (for Non-P Ne3200 systems).      *
; or  2. 14 byte MAC header (for Ne3200Promisc. systems).   *
;                                                               *
;---------------------------------------------------------------/

   mov   dx, BMIC_INDEX         ; Set index to channel 1
   mov   al, CH1_STROBE         ;  strobe register.
   out   dx, al
   mov   dx, BMIC_DATA         ; DX = BMIC Data port.
   out   dx, al            ; Strobe chan 1 (Start copy).


IFNDEF NE3200P   ; Non-P Ne3200 code

;---------------------------------------------------------------\
;                                                               *
; Set up to perform a Poke operation to inform the host (driver)*
; that the host ECB (RCB) is done having the RCB template plus   *
; the frame data copied into it. We notify the host by writing   *
; a Zero value into the host's AdapterRCBList pointer table.   *
;                                                               *
;---------------------------------------------------------------/

   mov   dx, BMIC_INDEX         ; Set index to Peek/Poke
   mov   al, PEEK_POKE_DATA OR AUTO_INC   ;  data register.
   out   dx, al

;;if SupportJabber
;;   mov   ax, cx            ; AX = Packet Length.
;;else
;;   sub   cx, size AReceiveBufferStructure
;;   mov   ax, cx            ; AX = Packet length.
;;endif
;;   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
;;   out   dx, al            ; Set Lo length.
;;   mov   al, ah            ; Set Hi length.
;;   out   dx, al
;;   mov   ax, es            ; Zero out hi word of dword.
;;   out   dx, al
;;   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov     ax, es                          ; AX = 0.
   out     dx, al                          ; Zero out lo word of dword.
   out     dx, al
   out     dx, al            ; Zero out hi word of dword.
   out     dx, al

   mov   bx, RCBListPointer + 2      ; BX -> host ECB (RCB) List.
   mov   ax, RCBIndex         ; AX = Index into list.
   shl   ax, 2            ; AX = Index * 4.
   add   ax, RCBListPointer      ; Add to base of list.
   adc   bx, 0            ; BX:AX ->Next ECB (RCB) slot.

   out   dx, al            ; Set addr 0 in BMIC
   mov   al, ah            ;   PEEK_POKE_ADDR register.
   out   dx, al            ; Set addr 1.

   mov   ax, bx
   out   dx, al            ; Set addr 2.
   mov   al, ah
   out   dx, al            ; Set addr 3.

   inc   RCBIndex         ; Increment host ECB(RCB) index.
   and   RCBIndex, TABLE_MASK      ; Wrap if needed.

;---------------------------------------------------------------\
;                                                               *
; Check if the RCB template and the frame data transfer is   *
; completed yet (before we later on start a poke operation).   *
;                                                               *
;---------------------------------------------------------------/

   mov   dx, BMIC_INDEX         ; Set index to channel 1
   mov   al, CH1_STATUS         ;  status register.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data port.

WaitForXferToHost:
   in   al, dx            ; AL = Current status.
   test   al, TRANSFER_ENABLED OR TRANSFER_IN_PROGRESS
   jnz   short WaitForXferToHost
   and   al, 00000011b         ; Mask out reserved bits.
   out   dx, al            ; Reset Status bit.


ELSE   ; Ne3200Promisc code

;***************************************************************\
;                                                               *
; Wait for the RCB template plus the MAC header transfer to   *
; the host ECB to complete.               *
;   bp = packet length in bytes.            *
;                        *
;***************************************************************/

   mov     dx, BMIC_INDEX                  ; Set index to channel 1
   mov     al, CH1_STATUS                  ;  status register.
   out     dx, al

;; LON   xor     cx, cx
   mov     dx, BMIC_DATA                   ; DX = BMIC Data port.
WaitForECBToHost:
   in      al, dx                          ; AL = Current status.
   test    al, TRANSFER_ENABLED OR TRANSFER_IN_PROGRESS ;
   jnz   short WaitForECBToHost      ; Jmp if transfer in progress.

;; LON   jz      WaitForECBToHostDone
;;   loopnz   WaitForECBToHost      ; Jmp if transfer in progress.
;;   StatisticsUpdate PacketRxMiscErrorCount
;;   jmp     ReStart586FromTx
;; WaitForECBToHostDone:

   and     al, 00000011b                   ; Mask out reserved bits.
   out     dx, al                          ; Reset Status bit.

;---------------------------------------------------------------\
;                                                               *
; Add TEMPLATE_MAC_SIZE to adjust BX/DI (i.e. pointers into the *
; host ECB space) to point to where the 1st byte of frame data   *
; is to be copied. The first frame data byte is copied into the   *
; host ECB (RCB) right after the MAC ethernet header.      *
;   di = Host RCB (ECB) address low            *
;   bx = Host RCB (ECB) address high         *
;                        *
;---------------------------------------------------------------/

   add     di, TEMPLATE_MAC_SIZE      ; DI->past MAC hdr in host ECB.
   adc     bx, 0                           ; Adjust high address.

   mov     si, FDHead                      ; SI -> Frame Descriptor.
   mov     si, [si].UFDRBDOffset           ; SI -> First RBD.
   mov     si, [si].RBDBufPtr              ; SI -> First Buffer.
   mov     cx, RBDSizeBeforeWrap           ; CX = Total Packet Size.

   mov     dx, BMIC_INDEX                  ; Set index to channel 1
   mov     al, CH1_TBI_BASE_ADDR OR AUTO_INC ; TBI base address register.
   out     dx, al

   mov     dx, BMIC_DATA                   ; DX = BMIC Data Port.
   mov     ax, si                          ; AX = Local address of frame.
   shr     ax, 1                           ; Adjust for the BMIC.
   out     dx, al                          ; Set LSB of local addr.
   mov     al, ah
   out     dx, al                          ; Set MSB of local addr.

   mov     dx, BMIC_INDEX                  ; Set index to channel 1
   mov     al, CH1_BASE_COUNT OR AUTO_INC  ;  base count register.
   out     dx, al

   mov     dx, BMIC_DATA                   ; DX = BMIC Data Port.
   mov     ax, cx                          ; AX = RBDSizeBeforeWrap.
   out     dx, al                          ; Set lo count.
   mov     al, ah
   out     dx, al                          ; Set hi count.

   mov     al, AUTO_START OR ADAPTER_TO_EISA
   out     dx, al

   mov     ax, di                          ; AX = host ECB addr low.
   out     dx, al
   mov     al, ah
   out     dx, al

   mov     ax, bx                          ; AX = host ECB addr high.
   out     dx, al
   mov     al, ah
   out     dx, al

   mov     dx, BMIC_INDEX                  ; Set index to Channel 1
   mov     al, CH1_STROBE                  ;  strobe register.
   out     dx, al
   mov     dx, BMIC_DATA                   ; DX = BMIC Data port.
   out     dx, al                          ; Strobe channel 1.

;---------------------------------------------------------------\
;                                                               *
; Wait for the second part of the rcv frame to complete its   *
; transfer to the host ECB. This is the last BMIC transfer   *
; needed to copy the whole frame to the host ECB, UNLESS   *
; the frame wrapped from the last data buffer to the first.   *
;                                                               *
;---------------------------------------------------------------/

   mov     dx, BMIC_INDEX                  ; Set index to channel 1
   mov     al, CH1_STATUS                  ;  status register.
   out     dx, al

;; LON   xor     cx, cx
   mov     dx, BMIC_DATA                   ; DX = BMIC Data port.
WaitForDataToHost:
   in      al, dx                          ; AL = Current status.
   test    al, TRANSFER_ENABLED OR TRANSFER_IN_PROGRESS
   jnz   short WaitForDataToHost

;; LON   jz      WaitForDataToHostDone
;;   loopnz  short WaitForDataToHost
;;   StatisticsUpdate PacketRxMiscErrorCount
;;   jmp     ReStart586FromTx
;; WaitForDataToHostDone:

   and     al, 00000011b                   ; Mask out reserved bits.
   out     dx, al                          ; Reset Status bit.

   cmp     RBDSizeAfterWrap, 0             ; Did we wrap?
   je      InformHostItsDone               ; Jump if not.

;---------------------------------------------------------------\
;                                                               *
; Copy the wrapped frame data to the host ECB.         *
;   di = Host RCB (ECB) address low            *
;   bx = Host RCB (ECB) address high         *
;                                                               *
;---------------------------------------------------------------/

   add     di, cx                          ; DI -> next position in ECB.
   adc     bx, 0                           ; Adjust Hi address.

   mov     dx, BMIC_INDEX                  ; Set index to channel 1
   mov     al, CH1_TBI_BASE_ADDR OR AUTO_INC ; TBI base address register.
   out     dx, al

   mov     dx, BMIC_DATA                   ; DX = BMIC Data Port.
   mov     ax, BufferHead                  ; AX = Local address.
   shr     ax, 1                           ; Adjust for the BMIC.
   out     dx, al                          ; Set LSB of local addr.
   mov     al, ah
   out     dx, al                          ; Set MSB of local addr.

   mov     dx, BMIC_INDEX                  ; Set index to channel 1
   mov     al, CH1_BASE_COUNT OR AUTO_INC  ;  base count register.
   out     dx, al

   mov     dx, BMIC_DATA                   ; DX = BMIC Data Port.
   mov     ax, RBDSizeAfterWrap            ; AX = remaining data bytes.
   out     dx, al                          ; Set lo count.
   mov     al, ah
   out     dx, al                          ; Set hi count.

   mov     al, AUTO_START OR ADAPTER_TO_EISA
   out     dx, al

   mov     ax, di                          ; AX = host ECB addr lo.
   out     dx, al
   mov     al, ah
   out     dx, al

   mov     ax, bx                          ; AX = host ECB addr hi.
   out     dx, al
   mov     al, ah
   out     dx, al

   mov     dx, BMIC_INDEX                  ; Set index to Channel 1
   mov     al, CH1_STROBE                  ;  strobe register.
   out     dx, al
   mov     dx, BMIC_DATA                   ; DX = BMIC Data port.
   out     dx, al                          ; Strobe chan 1 (start copy).

;---------------------------------------------------------------\
;                                                               *
; Wait for the wrapped data transfer to complete.      *
;                                                               *
;---------------------------------------------------------------/

   mov     dx, BMIC_INDEX                  ; Set index to channel 1
   mov     al, CH1_STATUS                  ;  status register.
   out     dx, al

;; LON   xor     cx, cx                          ; JCP, 941205. *Begin*
   mov     dx, BMIC_DATA                   ; DX = BMIC Data port.
WaitForXferToHost:
   in      al, dx                          ; AL = Current status.
   test    al, TRANSFER_ENABLED OR TRANSFER_IN_PROGRESS
   jnz   short WaitForXferToHost

;; LON   jz      short WaitForTransferToHostDone
;;   loopne  WaitForTransferToHost
;;   StatisticsUpdate PacketRxMiscErrorCount
;;   jmp     ReStart586FromTx
;; WaitForTransferToHostDone:                      ; JCP, 941205. *End*

   and     al, 00000011b                   ; Mask out reserved bits.
   out     dx, al                          ; Reset Status bit.

InformHostItsDone:

;---------------------------------------------------------------\
;                                                               *
; Setup for a Poke operation to inform the host driver that the   *
; ECB (RCB) is now complete. Informing the host is done by   *
; overwriting (poking) the host's physical ECB address (as con-   *
; tained in the circular queue, AdapterRCBList, see ne3200.386)   *
; with a value of Zero.                  *
;                                                               *
;---------------------------------------------------------------/

   mov     dx, BMIC_INDEX                  ; Set index to Peek/Poke
   mov     al, PEEK_POKE_DATA OR AUTO_INC  ;  data register.
   out     dx, al

   mov     ax, es                          ; AX = 0.
   mov     dx, BMIC_DATA                   ; DX = BMIC Data Port.
   out     dx, al                          ; Zero out lo word of dword.
   out     dx, al
   out     dx, al            ; Zero out hi word of dword.
   out     dx, al

   mov     bx, RCBListPointer + 2          ; BX -> host ECB (RCB) List.
   mov     ax, RCBIndex                    ; AX = Index into list.
   shl     ax, 2                           ; AX = Index * 4.
   add     ax, RCBListPointer              ; Add to base of list.
   adc     bx, 0                           ; BX:AX ->Next ECB(RCB) slot.

   out     dx, al                          ; Set addr 0.
   mov     al, ah
   out     dx, al                          ; Set addr 1.

   mov     ax, bx
   out     dx, al                          ; Set addr 2.
   mov     al, ah
   out     dx, al                          ; Set addr 3.

   inc     RCBIndex                        ; Increment index into host
                  ;  ECB (RCB) table.
   and     RCBIndex, TABLE_MASK            ; Wrap if needed.

ENDIF   ; End of Ne3200Promisc code


;---------------------------------------------------------------\
;                                                               *
; Perform the Poke operation to actually inform the host that   *
;   the ECB (RCB) is completely filled in and ready.      *
; Note: The Peek/Poke operation is started by writing to the   *
;   PEEK_POKE_CONTROL register (using the BMIC_DATA reg).   *
;                                                               *
;---------------------------------------------------------------/

   mov   dx, BMIC_INDEX         ; Set index to Peek/Poke
   mov   al, PEEK_POKE_CONTROL      ;  control register.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
;;;   mov   al, 01111111b         ;;    Set locked exchange to
   mov   al, 00111111b         ; Set poke (non-locked)
   out   dx, al            ;  host memory and start it.

   mov   dx, BMIC_STATUS         ; DX = BMIC Status port.

WaitForExchange:
   in   al, dx            ; AL = current status.
   test   al, PEEK_POKE_PENDING      ; Non-Locked exchange done?
   jnz   WaitForExchange         ; Jump if not.

   cmp   PollTimeout, 0         ;
   jne   RxDontInterruptHost      ;

   InterruptTheHost
   jmp   short RxGetNextRCBOffset   ;

RxDontInterruptHost:            ;
   mov   dx, BMIC_INDEX         ; Set index to the adapter
   mov   al, POLLING_MAILBOX      ;  poll mailbox.
   out   dx, al            ;
   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   in   al, dx            ; Read poll mailbox.
   or   al, al            ; Already waiting for poll?
   jne   RxGetNextRCBOffset      ; Jump if so.

   inc   al            ; Set Poll Mailbox.
   out   dx, al            ; Write a 1 to poll mailbox.

   mov   ax, PollTimeout         ; Set 80186 timer.
   mov   dx, T2MaxCount         ;
   out   dx, ax            ;

   mov   ax, 0c000h         ;
   mov   dx, T2Control         ;
   out   dx, ax            ;

RxGetNextRCBOffset:            ;
   mov   dx, BMIC_INDEX         ;
   mov   al, PEEK_POKE_ADDR OR AUTO_INC   ;
   out   dx, al            ;
   mov   dx, BMIC_DATA         ;

   mov   bx, RCBListPointer + 2      ; BX -> host ECB (RCB) List.
   mov   ax, RCBIndex         ; AX = Index into list.
   shl   ax, 2            ; AX = Index * 4.
   add   ax, RCBListPointer      ; Add to base of list.
   adc   bx, 0            ; BX:AX -> Next RCB slot.

   out   dx, al            ; Set addr 0.
   mov   al, ah            ;
   out   dx, al            ; Set addr 1.
   mov   ax, bx            ;
   out   dx, al            ; Set addr 2.
   mov   al, ah            ;
   out   dx, al            ; Set addr 3.
   mov   dx, BMIC_INDEX         ; Set index to Peek/Poke
   mov   al, PEEK_POKE_CONTROL      ;  control register.
   out   dx, al            ;
   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   al, 01011111b         ; Set peek at host memory
   out   dx, al            ;  and start it.
   mov   dx, BMIC_STATUS         ; DX = BMIC Status port.

WaitForExchange1:
   in   al, dx            ; AL = current status.
   test   al, PEEK_POKE_PENDING      ; Peek done?
   jnz   WaitForExchange1      ; Jump if not.

   mov   dx, BMIC_INDEX         ; Set index to Peek/Poke
   mov   al, PEEK_POKE_DATA OR AUTO_INC   ;  data register.
   out   dx, al

;---------------------------------------------------------------\
;                                                               *
; BMIC has completed the peek into the AdapterRCBList ptr table   *
; on the host side (HSM driver). So now copy in from the BMIC   *
; Peek Data register the address of the next empty ECB (RCB).   *
;                                                               *
;---------------------------------------------------------------/

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   di, offset NextEmptyRCB      ; DI -> Next Empty ECB (RCB).
   mov   cx, 4            ; Read the 4 byte ECB address.
 rep   insb

;---------------------------------------------------------------\
;                        *
; The receive frame processing is complete.         *
;                        *
; Now check if polling is enabled.            *
; During initialization the HSM driver provides us with a value   *
;   for PollTimeout. A zero value means no polling is wanted.   *
;                        *
;---------------------------------------------------------------/

   cmp   PollTimeout, 0         ; Is polling enabled?
   jne   RxCheckRCBCount         ; Jump if yes.
   ret               ; Done with rcv frame - Exit.


RxCheckRCBCount:         ; We just copied into one RCB
   dec   KnownRCBCount      ;   (ECB) so decrement the cnt.

;---------------------------------------------------------------\
;                        *
; When polling, we ought to have a reserve of 8 RCBs (ECBs)   *
; waiting for us to copy received frames into. Otherwise we need*
; to interrupt the host to get it processing the recv'd frames.   *
;                        *
;---------------------------------------------------------------/

   cmp   KnownRCBCount, 8   ; Are enough RCBs(ECB) left?
   jbe   GetRCBList      ; Jump if No.
   ret            ; Done - Exit.


GetRCBList:
   mov   si, offset RCBListTransferSequence

;---------------------------------------------------------------\
;                        *
; Copy from the host, the entire AdapterRCBList ptr table into   *
; our RCBListBuffer. Check if the host is keeping up with us.   *
;                        *
;---------------------------------------------------------------/

   BMICTransferWithLocal
   mov   KnownRCBCount, 0     ; Initialize to 0.
   mov   si, offset RCBListBuffer  ; SI -> RCB (ECB) ptr list.
   mov   cx, TOTAL_RCBS      ; TOTAL_RCBS = 10h.

CountRCBsLoop:            ;  (Note: lodsw increments SI.)
   lodsw            ; AX =low word of RCB(ECB) ptr.
   mov   bx, ax
   lodsw            ; AX =hi word of RCB(ECB) ptr.
   cmp   ax, -1         ; Currently unused entry if -1.
   je   CountNextRCB      ; Get next RCB(ECB) ptr entry.

   or   ax, ax         ; Is hi word of ptr Non-zero?
   jne   WeHaveAValidRCB      ; Jump if yes.

;----------------------------------------------------------------\
;                         *
; The high word of the RCB (ECB) pointer is zero. Now check if    *
; the low word of the pointer is also zero, meaning that we    *
; recently received this frame and already copied it to the host.*
; And to indicate to the host (HSM) that this received frame is    *
; waiting for the host to send it on up the protocol stack, we    *
; over-wrote the RCB (ECB) pointer with a value of zero.    *
;                         *
;----------------------------------------------------------------/

;;if SupportJabber         ; LON 2/15/96
;;   cmp     bx, 2048+14      ; Is low ptr wrd really a size?
;;else
;;   cmp     bx, 1514
;;endif
;;   jbe   CountNextRCB      ; Jump if yes.

   or   bx, bx         ; Is low word of ptr also zero?
   jz   CountNextRCB      ; Jump if yes

;---------------------------------------------------------------\
;                        *
; Indicate another valid pointer to an RCB (ECB) which we can   *
; use later to copy the next received frame into.      *
;                        *
;---------------------------------------------------------------/

WeHaveAValidRCB:
   inc   KnownRCBCount

CountNextRCB:
   loop   CountRCBsLoop

   cmp   KnownRCBCount, 8   ; Do we have enough RCBs(ECB)
   ja   GetRCBListOK      ;  in reserve? Jump if Yes.

   InterruptTheHost      ; Otherwise get the host going.

GetRCBListOK:
   ret            ; Return to FRInterrupt.



RxEIINotSupported:
   cmp   PromiscuousMode, 0
   je   GetRCBListOK         ; Ignore frm-Return to FRInterrupt.
   call   GetADefaultBoardNumber      ; In promiscuous - Try default.
   jmp   short RxEIINotSupportedProm   ; Continue rcv processing

;***************************************************************\
;                                                               *
; Ethernet II packet received. Make sure we support it and fill   *
; in the ECB protocol ID and board number field.      *
;                                                               *
; Note: On return DX is setup with an ECB offset to the first   *
;   frame data byte following the Type field.      *
;                        *
;***************************************************************/


RxEthernetII:
   mov   ax, es            ; AX = 0.
   stosw               ; Store Type as a PID in the
   stosw               ;  ARProtocolID RCB template.
   mov   ax, [bx+size AReceiveBufferStructure+12] ; Load Type field.
   stosw

   mov   ax, BoardNumberEII      ; AX = EII board number.
   cmp   ax, -1            ; Valid board number?
   jz   RxEIINotSupported      ; Jump if not (try a default?).

RxEIINotSupportedProm:
   stosw               ; Save Board # in RCB template.
   mov   ax, es            ; AX = 0.
   stosw               ; Store into RCB template.

   mov   dx, size ATransmitBufferStructure + 14   ; DX = offset into ECB buffer.

   jmp   RxBackFromOtherMedia      ; Continue processing.


;***************************************************************\
;                                                               *
; 802.2 packet received. Make sure a SNAP extension hasn't been   *
; added, make sure we support it and fill in the ECB protocol   *
; ID and board number field.               *
; 802.2      = /Len /DSAP /SSAP /UI(03h) /<data>.      *
; 802.2 SNAP = /Len /DSAP(AAh) /SSAP(AAh) /UI /<5 byte snap>...   *
;                                                               *
; Note: On return DX is setup with an ECB offset to the first   *
;   frame data byte following the 802.2 or 802.2 SNAP hdr.   *
;   The ECB offset is saved in the RCB template and is used   *
;   later by the host (HSM) when processing the rcv frame.   *
;                        *
;***************************************************************/

Rx8022:

IFNDEF NE3200P
   mov   byte ptr [bx].ARDriverWorkSpace + 1, 1   ; Assume 802.2 Type 1
   cmp   word ptr [bx+size AReceiveBufferStructure+14], 0AAAAh
   jne   RxMustBe8022         ; Jump if not snap.
   cmp   byte ptr [bx+size AReceiveBufferStructure+16], 03h ; UI value?
   je   Rx8022SNAP         ; Jump if yes.

RxMustBe8022:
   mov   dx, size ATransmitBufferStructure + 14 + 4  ; DX = offset in ECB buffer.
   inc   byte ptr [bx].ARDriverWorkSpace + 1   ; Assume 802.2 Type 2
   test   byte ptr [bx+size AReceiveBufferStructure+16], 00000001b
   je   RxMustBe8022Type2         ; Jump if I-Format PDU

   test   byte ptr [bx+size AReceiveBufferStructure+16], 00000010b
   je   RxMustBe8022Type2         ; Jump if S-Format PDU
   dec   byte ptr [bx].ARDriverWorkSpace + 1   ; 802.2 Type 1
   dec   dx
RxMustBe8022Type2:
   mov   ax, es            ; AX = 0.
   stosw               ; Store DSAP as a PID into
   stosw               ;  ARProtocolID RCB template.
   mov   ah, [bx+size AReceiveBufferStructure+14]  ; Load the DSAP.
   stosw
   mov   ax, BoardNumber8022      ; AX = 8022 board number.
   cmp   ax, -1            ; Valid board number?
   jz   Rx8022NotSupported      ; Jump if not (try a default?).

ELSE   ; Ne3200Promisc code
   mov     byte ptr [bx].ARDriverWorkSpace + 1, 1  ; Assume 802.2 Type 1
   cmp     word ptr [si+0], 0AAAAh
   jne     RxMustBe8022                            ; Jump if not snap.
   cmp     byte ptr [si+2], 03h
   je      Rx8022SNAP

RxMustBe8022:
   mov     dx, size ATransmitBufferStructure + 14 + 4 ; DX = offset in ECB buf.
   inc     byte ptr [bx].ARDriverWorkSpace + 1     ; Assume 802.2 Type 2
   test    byte ptr [si+2], 00000001b
   je      RxMustBe8022Type2                       ; Jump if I-Format PDU
   test    byte ptr [si+2], 00000010b
   je      RxMustBe8022Type2                       ; Jump if S-Format PDU

   dec     byte ptr [bx].ARDriverWorkSpace + 1     ; 802.2 Type 1
   dec   dx            ; DX =Offset to 1st data byte
                  ;  in host ECB buffer.

RxMustBe8022Type2:
   mov     ax, es                          ; AX = 0.
   stosw                                   ; Store DSAP as a PID into
   stosw                                   ;  ARProtocolID RCB template.
   mov     ah, [si+0]                      ; AH = 802.2 DSAP.
   stosw

   mov     ax, BoardNumber8022             ; AX = 8022 board number.
   cmp     ax, -1                          ; Valid board number?
   jz      Rx8022NotSupported              ; Jump if not (try a default?).
ENDIF

Rx8022NotSupportedProm:
   stosw               ; Save Board # in RCB template.
   mov   ax, es            ; AX = 0.
   stosw               ; Store into RCB template.
   jmp   RxBackFromOtherMedia      ; Continue processing.


Rx8022NotSupported:
   cmp   PromiscuousMode, 0      ; In promiscuous?
   je   RxSNAPNotSupportedExit      ; Jmp if no (Go exit).

   mov   word ptr es:[di], dx      ; Save DX temporarily in RCB.
   call   GetADefaultBoardNumber      ; In promiscuous - Try default.
   mov   dx, word ptr es:[di]      ; Restore DX.
   jmp   short Rx8022NotSupportedProm

;***************************************************************\
;                                                               *
; 802.2 SNAP packet received. Make sure we support it and fill   *
; in the ECB protocol ID and board number field.      *
;                                                               *
; Note: On return DX is setup with an ECB offset to the first   *
;   frame data byte following the 802.2 SNAP hdr.      *
;                        *
;***************************************************************/

Rx8022SNAP:

IFNDEF NE3200P
   mov   ax, es            ; AX = 0.
   mov   ah, [bx+size AReceiveBufferStructure+17]
   stosw               ; Store SNAP addr as PID in
   mov   ax, [bx+size AReceiveBufferStructure+18];  ARProtocolID.
   stosw
   mov   ax, [bx+size AReceiveBufferStructure+20]
   stosw

ELSE   ; Ne3200Promisc code
   mov     ax, es                          ; AX = 0.
   mov     ah, [si+3]
   stosw                                   ; Store SNAP addr as PID in
   mov     ax, [si+4]         ;  ARProtocolID RCB template.
   stosw
   mov     ax, [si+6]
   stosw
ENDIF

   mov   ax, BoardNumberSNAP      ; AX = EII board number.
   cmp   ax, -1            ; Valid board number?
   jz   RxSNAPNotSupported      ; Jump if not.
RxSNAPNotSupportedProm:
   stosw               ; Save Board # in RCB template.
   mov   ax, es            ; AX = 0.
   stosw               ; Store into RCB template.

   mov   dx, size ATransmitBufferStructure + 14 + 8; DX = Offset in ECB buffer.
   jmp   RxBackFromOtherMedia


RxSNAPNotSupported:
   cmp   PromiscuousMode, 0
   je   RxSNAPNotSupportedExit
   call   GetADefaultBoardNumber
   jmp   short RxSNAPNotSupportedProm

RxSNAPNotSupportedExit:
   ret               ; Return to FRInterrupt.

CopyPacketToHost   endp


;***********************************************************************\
;                                                                       *
; UpdateHostStatistics - Transfer statistic buffer to the host.      *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Destroyed         *
;   BX = Not Used         BX = Destroyed         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Destroyed         *
;   SI = Not Used         SI = Destroyed         *
;   DI = Not Used         DI = Destroyed         *
;                                                                       *
;***********************************************************************/
   even

UpdateHostStatistics   proc
   mov   ax, es            ; AX = 0.
   xchg   ax, ds:SCB.SCBCRCErrors      ; Swap with SCB.
   add   word ptr StatisticCounters.ChecksumErrorCount, ax
   mov   ax, es            ; AX = 0.
   adc   word ptr StatisticCounters.ChecksumErrorCount+2, ax

   xchg   ax, ds:SCB.SCBAlignErrors   ; Swap with SCB.
   add   word ptr StatisticCounters.RxAbortFrameAlignment, ax
   add   word ptr StatisticCounters.PacketRxMiscErrorCount, ax
   mov   ax, es            ; AX = 0.
   adc   word ptr StatisticCounters.RxAbortFrameAlignment+2, ax
   adc   word ptr StatisticCounters.PacketRxMiscErrorCount+2, ax

   xchg   ax, ds:SCB.SCBResources      ; Swap with SCB.
   add   word ptr StatisticCounters.PacketRxOverflowCount, ax
   mov   ax, es            ; AX = 0.
   adc   word ptr StatisticCounters.PacketRxOverflowCount+2, ax

   xchg   ax, ds:SCB.SCBOverruns      ; Swap with SCB.
   add   word ptr StatisticCounters.RxDMAOverrun, ax
   add   word ptr StatisticCounters.PacketRxMiscErrorCount, ax
   adc   word ptr StatisticCounters.RxDMAOverrun+2, 0
;; LON   adc   word ptr StatisticCounters.PacketRxMiscErrorCount, ax
   adc   word ptr StatisticCounters.PacketRxMiscErrorCount+2, 0

   mov   si, offset StatisticsSequence   ; SI -> Preset sequence.
   BMICTransferWithLocal
   mov   si, offset StatisticsSequence1
   BMICTransferWithLocal

   ret               ; Exit.

UpdateHostStatistics   endp


;***********************************************************************\
;                                                                       *
; SetupForMulticastCommand - Copy multicast table to SCB structure and   *
; initialize the remainder of the command block.         *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Destroyed         *
;   BX = Not Used         BX = @ MC Command Block      *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Destroyed         *
;   SI = Not Used         SI = Destroyed         *
;   DI = Not Used         DI = Destroyed         *
;                                                                       *
;***********************************************************************/
   even

SetupForMulticastCommand   proc

   mov   si, offset HostMulticastTable   ; SI -> Host table.
   mov   di, COMMAND_BUFFER + MulticastCmdAddresses
   mov   cx, MulticastCount      ; CX = Addresses in table.
   jcxz   AddressesCopied         ; Jump if No addresses.
CopyFromHostLoop:
   movsw               ; Copy six bytes.
   movsw
   movsw
   lodsw               ; Skip two bytes.
   loop   CopyFromHostLoop      ; Get next address.

AddressesCopied:
   mov   bx, COMMAND_BUFFER      ; BX -> Command Buffer.
   mov   [bx].MulticastCmdStatus, 0
   mov   [bx].MulticastCmdCommand, CB_MC_SETUP + ELBIT
   mov   [bx].MulticastCmdLink, 0
   mov   ax, MulticastCount      ; CX = MC addresses.
   mov   cl, 6            ; Multiply by 6.
   mul   cl
   mov   [bx].MulticastCmdCount, ax

   ret               ; Exit

SetupForMulticastCommand   endp

;***********************************************************************\
;                                                                       *
; Abend - Set BMIC mailbox 0 to error code and halt.         *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         (Never returns)         *
;   BX = Error Code                     *
;   CX = Not Used                     *
;   DX = Not Used                     *
;    BP = Not Used                     *
;   SI = Not Used                     *
;   DI = Not Used                     *
;                                                                       *
;***********************************************************************/
   even

Abend   proc

   mov   dx, BMIC_INDEX         ; Set index to Abend
   mov   al, ABEND_MAILBOX      ;  Mailbox register.
   out   dx, al

   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   ax, bx            ; AX = Error Code.
   out   dx, al            ; Set LSB of error code.
   mov   al, ah
   out   dx, al            ; Set MSB of error code.
   ;
   ; Fall thru to IdleAdapter.
   ;

Abend   endp

;***********************************************************************\
;                                                                       *
; IdleAdapter - Interrupt should already be disabled. Stay in      *
; continuous loop until host resets us.               *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         (Never returns)         *
;   BX = Not Used                     *
;   CX = Not Used                     *
;   DX = Not Used                     *
;    BP = Not Used                     *
;   SI = Not Used                     *
;   DI = Not Used                     *
;                                                                       *
;***********************************************************************/
   even

IdleAdapter      proc

   jmp   short IdleAdapter

IdleAdapter      endp


ForceToMaxMulticast:
   mov   cx, MAX_MULTICAST_ADDR
   jmp   short MulticastBetweenZeroAndMax

;***********************************************************************\
;                                                                       *
; UpdateParameters - Read the parameter block from the host and redo   *
; our mutlicast table. Next reinitialize control structures and      *
; reconfigure the 82586.                   *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Destroyed         *
;   BX = Not Used         BX = Destroyed         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Destroyed         *
;   SI = Not Used         SI = Destroyed         *
;   DI = Not Used         DI = Destroyed         *
;                                                                       *
;***********************************************************************/
   even

UpdateParameters   proc

;---------------------------------------------------------------\
;                                                               *
; Copy the adapter parameter block from host.         *
;                                                               *
;---------------------------------------------------------------/

   mov   si, offset ParameterTransferSequence   ; SI -> Source.

   BMICTransferWithLocal         ; Copy adapter parm block.

   mov   cx, MulticastCount      ; CX = address count.
   jcxz   TableTransferedFromHost      ; Finished if empty.
   cmp   cx, MAX_MULTICAST_ADDR      ; Over max?
   ja   ForceToMaxMulticast      ; Jump if so.

MulticastBetweenZeroAndMax:

;---------------------------------------------------------------\
;                                                               *
; Copy the multicast table from host.            *
;                                                               *
;---------------------------------------------------------------/

   shl   cx, 3            ; CX = CX * 8.
   mov   MCTableCount, cx      ; Save the count into sequence.
   mov   ax, MulticastListPointer   ; Save the host address.
   mov   MCTableOffsetLo, ax
   mov   ax, MulticastListPointer + 2
   mov   MCTableOffsetHi, ax
   mov   si, offset MCTableSequence   ; SI -> sequence.

   BMICTransferWithLocal

TableTransferedFromHost:

   cmp   StatisticCounters.CustomCounterCount, 0   ; First time thru?
   jz   DontAddMulticast      ; Jump if so.

IFNDEF NE3200P
if SupportJabber

;---------------------------------------------------------------\
;                                                               *
; Note: While attempting to make the Non-Promisc. code      *
; consistently handle 2000 or 2048 byte frames (i.e. always   *
; copy the frame into a rcv buffer and not ignore it when the   *
; PRM and SAV BF bits are configured, we attempted to make it   *
; work by skipping Initialize586 and instead using the NE3200P's*
; method of aborting the RU and then re-configuring the 82586.   *
; It did not work and in the TESTCON Good/Bad Promiscuous test,   *
; the 82586 processed rcv frames much more slowly.      *
;                                                               *
;---------------------------------------------------------------/

   jmp   short Initialize586      ; Jmp if Yes.

else
UpdatePromiscConfig:
   call    SetupForMulticastCommand        ; Set up command block.
   mov     ax, 140h         ; Abort the RU and issue
   call    Command586NoRestart      ;  multicast change command.
   jne     WaitForMCError         ; Jump if 586 locked.

   mov   cx, es                          ; Set loop counter (Init to 0).
WaitForMCCommandLoop:
   test    word ptr [bx], 8000h            ; Command complete?
   jnz     EnableRU                        ; Jump if so.
   loop    WaitForMCCommandLoop            ; Loop if not.
WaitForMCError:
   call    ReStart586         ; Exit - error.
   ret

EnableRU:
   mov     si, offset ConfigNormal         ; SI -> Config parms.
   cmp     PromiscuousMode, 0              ; Promiscuous mode on?
   je      UpdateConfigNotProm             ; Jump if not.
   mov     si, offset ConfigPromiscuous    ; SI -> Config parms.
UpdateConfigNotProm:
   mov     di, COMMAND_BUFFER              ; DI -> command buffer.
   mov     cx, ConfigNormalSize / 2  ; TNL 7/25/95 CX = words to move.
   mov     bx, di                          ; BX -> command buffer.
 rep    movsw

   mov     ax, 100h         ; SCB 100 = start exec of CBL
   call    Command586NoRestart             ; Issue Command.
   jnz     short UpdateConfigDone          ; Jump if error.
   mov     cx, es                          ; Set loop counter (Init to 0).
WaitForConfigUpdateLoop:
   test    word ptr [bx], 8000h            ; Command complete?
   jnz     UpdateConfigExit                ; Jump if so.
   loop    WaitForConfigUpdateLoop         ; Loop if not.
   or      al, 1                           ; Set error flag.
UpdateConfigExit:
   jmp     ProcessFRInterrupt              ; In case we Rx'd something
                  ;  and start the RU.
endif

ELSE   ; Ne3200Promisc code
   call    SetupForMulticastCommand        ; Set up command block.

   mov     ax, 140h         ; Abort the RU and issue
   call    Command586NoRestart      ;  multicast command.
   jne     WaitForMCError         ; Jump if 586 locked.

   mov     cx, es                          ; Set loop counter (Init to 0).
WaitForMCCommandLoop:
   test    word ptr [bx], 8000h            ; Command complete?
   jnz     EnableRU                        ; Jump if so.
   loop    WaitForMCCommandLoop            ; Loop if not.
WaitForMCError:
   call    ReStart586
   ret               ; Exit.

EnableRU:
   mov     si, offset ConfigNormal         ; SI -> Config parms.
   cmp     PromiscuousMode, 0              ; Promiscuous mode on?
   je      UpdateConfigNotProm             ; Jump if not.
   mov     si, offset ConfigPromiscuous    ; SI -> Config parms.
UpdateConfigNotProm:
   mov     di, COMMAND_BUFFER              ; DI -> command buffer.
   mov     cx, ConfigNormalSize / 2  ; TNL 7/25/95 CX = words to move.
   mov     bx, di                          ; BX -> command buffer.
 rep    movsw

   mov     ax, 100h         ; SCB 100 = start exec of CBL
   call    Command586NoRestart             ; Issue Command.
   jnz     short UpdateConfigExit          ; Jump if error.
   mov     cx, es                          ; Set loop counter (Init to 0).
WaitForConfigUpdateLoop:
   test    word ptr [bx], 8000h            ; Command complete?
   jnz     UpdateConfigExit                ; Jump if so.
   loop    WaitForConfigUpdateLoop         ; Loop if not.
   or      al, 1                           ; Set error flag.
UpdateConfigExit:
   jmp     ProcessFRInterrupt              ; In case we Rx'd something
                  ;   and start the RU Unit.

ENDIF


DontAddMulticast :
   ret               ; Exit.

UpdateParameters   endp


;***********************************************************************\
;                                                                       *
; 82586 specific code.                     *
;                                                                       *
;***********************************************************************/
;
;***********************************************************************\
;                                                                       *
; Initialize586 - Reset and configure the 82586, initialize the      *
; transmit buffers, initialize the receive buffers and set the deadman   *
; counters.                        *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Destroyed         *
;   BX = Not Used         BX = Destroyed         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Destroyed         *
;   SI = Not Used         SI = Destroyed         *
;   DI = Not Used         DI = Destroyed         *
;                                                                       *
; The Zero flag (ZF) is set according to results of RUStart when called   *
; by InitializeRxBuffers (ZeroFlag = 1 if successful).         *
;                                                                       *
;***********************************************************************/
   even

Initialize586   proc

;---------------------------------------------------------------\
;                                                               *
; Reset the 82586.                  *
;                                                               *
;---------------------------------------------------------------/

   ResetThe82586            ; Macro to reset 82586.

;---------------------------------------------------------------\
;                                                               *
; Configure 586 and setup Tx and Rx buffers.         *
;                                                               *
;---------------------------------------------------------------/
   call   Configure586
   jnz   Initialize586         ; Try again if error.

   mov   DeadMan, 0         ; Set deadman
   mov   DeadManHigh, 35         ;  counters to max.

   ret               ; Exit.
Initialize586   endp


;***********************************************************************\
;                                                                       *
; Configure586 - Send nop command to start 586, set up config command   *
; and iasetup command.                     *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Destroyed         *
;   BX = Not Used         BX = Destroyed         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Destroyed         *
;   SI = Not Used         SI = Destroyed         *
;   DI = Not Used         DI = Destroyed         *
;                                                                       *
;***********************************************************************/
   even

Configure586   proc

;---------------------------------------------------------------\
;                                                               *
; Zero out the memory that the 82586 will use for Tx and Rx   *
; buffers.                     *
;                                                               *
;---------------------------------------------------------------/

   mov   di, MEMORY_586_START      ; DI -> 8000h.
   mov   cx, MEMORY_SIZE / 2      ; CX -> 8000h / 2.
   mov   ax, es            ; AX =  0000h.
 rep   stosw               ; Zero out block.

;---------------------------------------------------------------\
;                                                               *
; Build up initial SCP and ISCP before starting the 82586.   *
;                                                               *
;---------------------------------------------------------------/

   mov   si, offset InitialSCP      ; Build our SCP.
   mov   di, SCP
   mov   cx, SCP_LENGTH / 2
 rep   movsw

   mov   di, offset ISCP         ; Build our ISCP.
   mov   cx, ISCP_LENGTH / 2
 rep   movsw

;---------------------------------------------------------------\
;                                                               *
; Send NOP command to the 82586 to start it.         *
;                                                               *
;---------------------------------------------------------------/

   mov   ax, es            ; 0 = NOP command.
   mov   ds:SCB.SCBCommand, ax      ; Set command.
   mov   bx, ax
   call   Command586NoRestart      ; Issue CU NOP Command.
   jnz   short NOPCommandError      ; Jump if error.
   mov   cx, es            ; Set loop counter (Init to 0).
WaitForNOPLoop:
   test   ds:SCB.SCBStatus, 8000h      ; CX bit set?
   jne   IssueConfigLoopbackCommand   ; Jump if so.
   loop   WaitForNOPLoop         ; Loop if not.
NOPCommandError:
   ret               ; Exit (error).

;---------------------------------------------------------------\
;                                                               *
; Send configure command to the 82586 placing it into internal   *
; loopback.                     *
;                                                               *
;---------------------------------------------------------------/

IssueConfigLoopbackCommand:

   mov   si, offset ConfigLoopback   ; SI -> Config parms.
   mov   di, COMMAND_BUFFER      ; DI -> Command buffer.
   mov   cx, ConfigNormalSize / 2  ;TNL 7/25/95 CX = words to move.
   mov   bx, di            ; BX -> Command buffer.
 rep   movsw

   mov   ax, 100h
   call   Command586NoRestart      ; Issue Configure Command.
   jnz   short ConfigCommandError   ; Jump if error.
   mov   cx, es            ; Set loop counter (Init to 0).
WaitForConfigLoop:
   test   word ptr [bx], 8000h      ; Command complete?
   jnz   IssueIACommand         ; Jump if so.
   loop   WaitForConfigLoop      ; Loop if not.
   or   al, 1            ; Set error flag.
ConfigCommandError:
   ret               ; Exit (error).

;---------------------------------------------------------------\
;                                                               *
; Send iasetup (ia = Individual node Address) command to 82586.   *
;                                                               *
;---------------------------------------------------------------/

IssueIACommand:
   mov   bx, COMMAND_BUFFER      ; BX -> command buffer.
   mov   di, bx            ; DI -> command buffer.
   mov   ax, es            ; AX = 0.
   stosw               ; Setup for IA command.
   mov   word ptr [di], 8001h
   inc   di
   inc   di
   stosw

   mov   si, offset BoardID      ; SI -> Node Address.
   movsw
   movsw
   movsw

   mov   ax, 100h
   call   Command586NoRestart      ; Issue IA-SETUP Command.
   jnz   IACommandError
   mov   cx, es            ; Set loop counter (Init to 0).
WaitForIALoop:
   test   word ptr [bx], 8000h      ; Command complete?
   jnz   IssueMCCommand         ; Jump if so.
   loop   WaitForIALoop         ; Loop if not.
   or   al, 1            ; Set error flag.
IACommandError:
   ret               ; Exit (error).

;---------------------------------------------------------------\
;                                                               *
; Send MC setup command to the 82586.            *
;                                                               *
;---------------------------------------------------------------/

IssueMCCommand:
   call   SetupForMulticastCommand   ; Set up block.
   mov   ax, 100h         ; AX = Start command.
   call   Command586NoRestart      ; Issue MC-SETUP Command.
   jnz   IACommandError         ; Exit if locked.

   mov   cx, es            ; Set loop counter (Init to 0).
WaitForMCLoop:
   test   word ptr [bx], 8000h      ; Command complete?
   jnz   IssueConfigNormalCommand   ; Jump if so.
   loop   WaitForMCLoop         ; Loop if not.
   or   al, 1            ; Set error flag.
   ret               ; Exit (error).

;---------------------------------------------------------------\
;                                                               *
; Issue a normal configure command to the 82586 to take it out   *
; of internal loopback. Then setup Tx & Rx buffers and start RU.*
;                                                               *
;---------------------------------------------------------------/

IssueConfigNormalCommand:
   mov   si, offset ConfigNormal      ; SI -> Config parms.
   cmp   PromiscuousMode, 0      ; Promiscuous mode on?
   je   IssueConfigNotProm      ; Jump if not.
   mov   si, offset ConfigPromiscuous   ; SI -> Config parms.
IssueConfigNotProm:
   mov   di, COMMAND_BUFFER      ; DI -> command buffer.
   mov   cx, ConfigNormalSize / 2   ;TNL 7/25/95 CX =Words to move.
   mov   bx, di            ; BX -> command buffer.
 rep   movsw

   mov   ax, 100h
   call   Command586NoRestart      ; Issue Configure Command.
   jnz   short ConfigNormalCommandError   ; Jump if error.

;;;;;
   mov   si, offset ConfigNormal      ; SI -> Config parms.
   cmp   PromiscuousMode, 0      ; Promiscuous mode on?
   je   IssueConfigNotPromisc      ; Jump if not.
   mov   si, offset ConfigPromiscuous   ; SI -> Config parms.
IssueConfigNotPromisc:
   mov   di, COMMAND_BUFFER      ; DI -> command buffer.
   mov   cx, ConfigNormalSize / 2   ;TNL 7/25/95 CX =Words to move.
   mov   bx, di            ; BX -> command buffer.
 rep   movsw

   mov   ax, 100h
   call   Command586NoRestart      ; Issue Configure Command.
   jnz   short ConfigNormalCommandError   ; Jump if error.


   call   InitializeTxBuffers      ;
   call   InitializeRxBuffers      ; (Jumps to RUStart).
   jnz   ConfigNormalCommandError   ; Jump if RUStart failed.

   mov   bx, COMMAND_BUFFER      ; Restore BX -> Command buffer.
   mov   cx, es            ; Set loop counter (Init to 0).
WaitForRUstartLoop:
   test   word ptr [bx], 8000h      ; Start RU Command complete?
   jnz   ExitConfig         ; Jump if so.
   loop   WaitForRUstartLoop      ; Loop if not.
   or   al, 1            ; Set error flag.

ConfigNormalCommandError:
   ret               ; Exit.

ExitConfig:
   xor   ax, ax
   ret               ; Exit.

Configure586   endp

;***********************************************************************\
;                                                                       *
; InitializeTxBuffers - Initialize the transmit buffers in the 82586's   *
; buffer space.                        *
;                           *
; TxBuffers are layed out in the following contiguous order:      *
;     1. The 82586 Transmit Command Block (TxCStruct),         *
;     2. The 82586 Transmit Buffer Descriptor (BDStruct),      *
;     3. The actual data frame sp (1514 or 2048 + extra space for BMIC).*
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Destroyed         *
;   BX = Not Used         BX = Destroyed         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Preserved         *
;   SI = Not Used         SI = Preserved         *
;   DI = Not Used         DI = Destroyed         *
;                                                                       *
;***********************************************************************/
   even

InitializeTxBuffers   proc

   mov   TxCHead, 0         ; Clear Tx Head pointer.
   mov   TxCEnd, 0         ; Clear Tx Tail pointer.
   mov   di, TX_BUFFERS         ; DI -> Tx buffers start adr.
   mov   cx, NUMBER_TX_BUFFERS      ; CX = number of Tx buffers.
   mov   TxFreeHead, di         ; Initialize the free head.

;---------------------------------------------------------------\
;                        *
; Setup the 82586's Transmit Command Block (i.e. TxCStruct)   *
;                        *
;---------------------------------------------------------------/

TxInitLoop:
   mov   [di].TxStatus, 0      ; Clear status.
   mov   [di].TxCmd, 0A004h      ; EL & interrupt (4=Xmit Cmd).
   mov   bx, di
   add   bx, size TxCStruct      ; BX -> BDStruct (i.e. TBD).
   mov   [di].TBDOffset, bx      ; Save TransmitBufferDesc adr.
   mov   ax, bx
   add   ax, size BDStruct
   mov   [bx].BufPtr, ax         ; Save ptr. to 1st frame byte.

ifdef   DAVID3
   add   ax, TX_BUFFER_SZ_MAX
else
   add   ax, TX_BUFFER_SIZE
endif
   mov   [di].TxCLink, ax      ; Save ptr to next TxCStruct.
   mov   di, ax            ; DI -> Next TxCStruct.

;---------------------------------------------------------------\
;                        *
; Setup the 82586's Transmit Buffer Descriptor (i.e. BDStruct)   *
;                        *
; NOTE: [bx].BufSize is not part of the true 82586 TBD.      *
;                        *
;---------------------------------------------------------------/

   mov   ax, es            ; AX = 0000h.
   mov   [bx].BDStatus, ax      ; Clear Status.
   mov   [bx].BDLink, ax         ; Clear link.
   mov   [bx].BufPtr + 2, ax      ; Clear high offset.

ifdef   DAVID3
   mov   [bx].BufSize, TX_BUFFER_SZ_MAX   ; Set buffer size.
else
   mov   [bx].BufSize, TX_BUFFER_SIZE   ; Set buffer size.
endif
   loop   TxInitLoop

   mov   [bx - size TxCStruct].TxCLink, ax   ; Clear last link.
   ret

InitializeTxBuffers   endp

;
;***********************************************************************\
;                                                                       *
; InitializeRxBuffers -                     *
;                           *
; Fields in the RCB template are not cleared(e.g. ARPacketLength) since *
; the memory test zeroed out all memory during early initialization.   *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Destroyed         *
;   BX = Not Used         BX = Destroyed         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Preserved         *
;   SI = Not Used         SI = Destroyed         *
;   DI = Not Used         DI = Destroyed         *
;                                                                       *
;***********************************************************************/
   even

InitializeRxBuffers   proc

IFNDEF NE3200P
   mov   ax, MEMORY_END         ; AX = Memory left over for
   sub   ax, offset RX_BUFFERS      ;  receive UniRcvBuf buffers.
   mov   dx, es            ; DX = 0 for divide.
   mov   bx, SIZE UniReceiveBuf      ; BX = Size of each UniRcvBuf.
                  ;    = 22 + 1558 + 10 = 1590.

if SupportJabber
   mov     si, (RBuffSize-14)      ; RBuffSize = 1500 + 14.
   cmp     PromiscuousMode, 0              ; Promiscuous mode on?
   je      BufferSizeNotProm               ; Jump if not.
                  ;  Note: MAX_JABBER = 2048+14.
   add     bx, MAX_JABBER - RBuffSize      ; Add enough for jabber (+548).
   mov     si, (MAX_JABBER-14)             ; SI = 2048.

BufferSizeNotProm:
endif
   div   bx            ; AX = Total UniRcvBuf buffers.
                  ;
   mov   di, RX_BUFFERS         ; DI -> Start of UniRcvBuffers.
   mov   FDHead, di         ; Save as head of list.
   mov   cx, ax            ; CX = Total UniRcvBuffers cnt.

;-----------------------------------------------------------------\
;                          *
; Each UniRcvBuffer (Uni means One) contains all the descriptors  *
; and buffers needed to hold one entire receive frame. The first  *
; part of a UniRcvBuf is the FD (Frame Desc), followed by the     *
; RCB template, which gets copied to the host to fill in an ECB.  *
; Next is the frame header and data (data buffer). The last part  *
; is the RBD (Receive Buffer Descriptor). Note the data buffer     *
; size is 1500 bytes or 2048 if jabbering is enabled.        *
;                          *
;-----------------------------------------------------------------/

RxInitLoop:
   mov   ax, es            ; AX = 0000h.
   mov   [di].UFDStatus, ax      ; Zero the status in FD.
   mov   [di].UFDCommand, ax      ; Clear command in FD.

if SupportJabber
   lea   bx, [di].DataBuffer      ; Address of data buffer.
   add   bx, si            ; SI = 2048 (data buffer size).
   mov   [di].URBDOffset, bx      ; Point to start of RBD.
   lea   dx, [di + SIZE UniReceiveBuf-1500]  ; DX -> Next UniRcvBuffer.
   add   dx, si            ; Add data buffer size(jabber).
else
   lea   bx, [di].RBDStatus         ; BX -> RBD struct.
   mov   [di].URBDOffset, bx         ;
   lea   dx, [di + SIZE UniReceiveBuf]      ; DX -> Next UniRcvbuf.
endif
   mov   [di].UFDLink, dx         ; Save ptr. to next
                     ;  UniRcvBuf struc.
   mov   word ptr [di].URCB.ARFragmentCount, 1   ; Force a frag count.

if SupportJabber
   lea   bx, [di].DataBuffer      ; BX -> Data Buffer.
   mov   di, bx            ; DI -> Data Buffer.
   add   di, si            ; DI -> RBD.
   mov   word ptr [di+0], ax      ; Clear EOF, F and ActualCount.

;-----------------------------------------------------------------\
;                          *
; Note: Every RCB points to the same DummyRCB as the next RCB link*
; because of an 82586 chip errata where an extra RBD prefetch is  *
; done when EL (End of List) = 1.              *
;                          *
; Note: Use BDStruct to reference the RBD in UniReceiveBuf struc. *
;                          *
;-----------------------------------------------------------------/

   mov   word ptr [di+2], DUMMY_RCB   ; Fill in RBDLink.
;;   mov   word ptr [di+2], 0ffffh      ;;  Fill in RBDLink.
   mov   word ptr [di+4], bx      ; Fill in RBDBufPtr.
   mov   word ptr [di+6], ax      ; Zero out RBDBufPtr+2.
   mov   word ptr [di+8], si      ; Fill in RBDBufSize.
   or   word ptr [di+8], 8000h      ; Set EL bit (End of List).
else
   mov   [di].RBDStatus, ax      ; Clear EOF, F and ActualCount.
   lea   bx, [di].DataBuffer      ; BX -> Data buffer.
   mov   [di].RBDBufPtr, bx      ; Save pointer.
   mov   [di].RBDBufPtr + 2, ax      ; Zero high word.
   mov   [di].RBDLink, DUMMY_RCB
   mov   [di].RBDBufSize, (RBuffSize-14) or 8000h ; Size and EL.
endif
   mov   di, dx            ; DI -> Next UniRcvBuffer.
   loop   RxInitLoop

if SupportJabber
   sub   di, SIZE UniReceiveBuf-1500   ; DI -> Last UniRcvBuffer.
   sub   di, si
else
   sub   di, SIZE UniReceiveBuf      ; DI -> Last UniRcvBuffer.
endif
   mov   FDEnd, di         ; FDEnd -> Last UniRcvBuffer.
   mov   [di].UFDCommand, 8000h      ; Set EL (End of List) bit.
   mov   ax, FDHead         ; Make the last UniRcvBuffer
   mov   [di].UFDLink, ax      ;   point to the first.

   mov   di, DUMMY_RCB         ; Fill in Dummy RCB.
   mov   [di].BDStatus, 0
   mov   [di].BDLink, -1         ; Indicate this is last RBD.
   mov   [di].BufPtr, offset DUMMY_BUFFER; Point to zeroed memory.
   mov   [di].BufPtr+2, 0
   mov   [di].BufSize, 8000h      ; Set EL (End of List) bit.

ELSE   ; Ne3200Promisc code

;***************************************************************\
;                        *
; Figure out how many FD's, RBD's and Buffers we can allocate.   *
; We first set up the FD's in one contiguous memory block. Then   *
; the RBD's are next set up as a contiguous block. Last the   *
; data buffers(256 in size) are also set up as a contiguous blk.*
;                        *
;***************************************************************/

   mov     ax, MEMORY_END                  ; AX = Memory left over for
   sub     ax, offset RX_BUFFERS           ;  receive frame area.
   mov     dx, es                          ; DX = 0 for divide.
   mov     bx, size UFDStruct + size RBDStruct + 256
                  ; BX = 22 + 10 + 256 = 288.
   div     bx                              ; AX = Total Receive buffers.
   mov     cx, ax
;---------------------------------------------------------------\
;                        *
; Link up the Frame Descriptors (FDs).            *
;                        *
;---------------------------------------------------------------/

   push    cx                              ; Save count.
   mov     di, RX_BUFFERS                  ; DI -> FD Area.
   mov     FDHead, di                      ; Initialize FD head pointer.

   mov     al, size UFDStruct              ; AL = Size of Frame Desc.
   mul     cl                              ; CL = Number of FD's.
   mov     dx, ax
   add     dx, di                          ; DX -> First RBD.
   mov     ax, es                          ; AX = 0.
LinkUpFDsLoop:
   mov     [di].UFDStatus, ax              ; Give ownership to 82586.
   mov     [di].UFDCommand, ax             ; Not End of List(EL), don't suspend.
   mov     si, di
   add     si, size UFDStruct              ; SI -> Next FD.
   mov     [di].UFDLink, si                ; Link to next FD.
   mov     [di].UFDRBDOffset, dx           ; First RBD or Invalid (-1).
   mov     di, si                          ; DI -> Next FD.
   mov     dx, -1                          ; A -1 tells the 82586 that no
                  ;  RBDs are linked to this FD.
   loop    LinkUpFDsLoop         ;
   pop     cx            ; Restore total count of FDs.

;---------------------------------------------------------------\
;                        *
; Set End of List bit (EL) in last Frame Descriptor and link it   *
; to the first Frame Descriptor (FD).            *
;                        *
;---------------------------------------------------------------/
;
   sub     di, size UFDStruct
   mov     FDEnd, di                       ; FDEnd -> Last Frame Desc(FD).
   mov     [di].UFDCommand, 8000h      ; Set EL (End of List) bit.
   mov     ax, FDHead                      ; Link the last Frame Desc to
   mov     [di].UFDLink, ax                ;   the first one.

;---------------------------------------------------------------\
;                        *
; Link up the Receive Buffer Descriptors (RBD) and data buffers.*
;  Rcv frames can consist of multiple data buffers.      *
;                        *
;---------------------------------------------------------------/

   mov     di, si                          ; DI -> RBD Area.
   mov     RBDHead, di                     ; Setup pointer to RBD head.
   mov     al, size RBDStruct              ; AL = Size of one RBD.
   mul     cl                              ; CL = Number of RBDs(previously saved).
   mov     dx, ax                          ; DX = Total size of RBD pool.
   add     dx, di                          ; DX -> 1st data buffer.
   mov     ax, es                          ; AX = 0.
LinkUpRBDsLoop:
   mov     [di].RBDStatus, 0               ; Clear EOF, F and ActualCount.
   add     si, size RBDStruct              ; SI -> Next RBD.
   mov     [di].RBDLink, si                ; Link in next RBD.
   mov     [di].RBDBufPtr + 0, dx          ; Point to RBD's data buffer.
   mov     [di].RBDBufPtr + 2, ax
   mov     [di].RBDBufSize, 256            ; 256 is size of data buffer.
   mov     di, si                          ; DI -> Next RBD.
   add     dx, 256            ; Point to next data buffer.
   loop    LinkUpRBDsLoop

;---------------------------------------------------------------\
;                        *
; Set Last RBD bit and link it to the dummy RCB. This is done   *
; due to 82586 D-Step Errata 2.3 (Extra RBD Prefetch when EL=1).*
;                        *
;---------------------------------------------------------------/

   mov     BufferHead, di                  ; DI -> 1st Buffer.
   sub     di, size RBDStruct              ; DI -> Last RBD.
   mov     RBDEnd, di                      ; RBDEnd -> Last RBD.
   or      [di].RBDBufSize, 8000h          ; Set End of List (EL).
   mov     [di].RBDLink, DUMMY_RCB         ; Link to dummy RBD.

   mov     di, DUMMY_RCB                   ; Fill in Dummy RCB.
   mov     [di].RBDStatus, 0      ; Clear EOF, F and ActualCount.
   mov     [di].RBDLink, -1                ; Indicate this is last RBD.
   mov     [di].RBDBufPtr, offset DUMMY_BUFFER; Point to zeroed memory.
   mov     [di].RBDBufPtr+2, 0
   mov     [di].RBDBufSize, 8000h          ; Set EL bit.

   lea     di, RCB_TEMPLATE      ; DI -> RCB structure template.
   mov   word ptr [di].ARFragmentCount,1   ; Ensure cnt will be 1 in host ECB.
ENDIF

;---------------------------------------------------------------\
;                        *
; Tell the 82586 Chip to start the Receive Unit (RU).      *
;                        *
;---------------------------------------------------------------/

   jmp   RUStart            ; Start receive unit.

;;   ret

InitializeRxBuffers   endp

;
;***********************************************************************\
;                                                                       *
; Command586NoRestart - Wait for any previous commands to finish and   *
; start the next one. Return if the previous command is not cleared.   *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = SCB Command      AX = Preserved         *
;   BX = @ Command Block      BX = Preserved         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Preserved         *
;   SI = Not Used         SI = Preserved         *
;   DI = Not Used         DI = Preserved         *
;               Zero flag set if successful.   *
;                                                                       *
;***********************************************************************/
   even

Command586NoRestart   proc

   mov   cx, es            ; Set loop counter (Init to 0).
WaitForPreviousLoop:
   cmp   ds: SCB.SCBCommand, 0      ; Command finished.
   loopne   WaitForPreviousLoop      ; Loop if not.
   jne   Command586Exit         ; 586 might be hosed.

   mov   ds: SCB.SCBCommand, ax      ; Set 586 command.
   mov   ds: SCB.CBLOffset, bx      ; Set command block pointer.

   mov   dx, CA_PORT         ; Tug on the 82586's
   out   dx, al            ;  channel attention.

Command586Exit:
   ret               ; Exit.

Command586NoRestart   endp
;
;***********************************************************************\
;                                                                       *
; Command586 - Wait for any previous commands to finish and start the   *
; next one. Restart the 82586 if the previous command didn't clear.   *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = SCB Command      AX = Preserved         *
;   BX = @ Command Block      BX = Preserved         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Preserved         *
;   SI = Not Used         SI = Preserved         *
;   DI = Not Used         DI = Preserved         *
;               Zero flag set if successful.   *
;                                                                       *
;***********************************************************************/
   even

Command586   proc

   mov   cx, es            ; Set loop counter (Init to 0).
WaitForLastCommandLoop:
   cmp   ds: SCB.SCBCommand, 0      ; Command finished.
   loopne   WaitForLastCommandLoop      ; Loop if not.
   jne   ReStart586         ; 586 might be hosed.

   mov   ds: SCB.SCBCommand, ax      ; Set 586 command.
   mov   ds: SCB.CBLOffset, bx      ; Set command block pointer.

   mov   dx, CA_PORT         ; Tug on the 82586's
   out   dx, al            ;  channel attention.

   ret               ; Exit.

Command586   endp

;***********************************************************************\
;                                                                       *
; ReStart586 - Reset the 82586, re-configure it, start the transmit   *
; unit if the transmit list contains pending transmits and re-load   *
; the multicast table.                     *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Destroyed         *
;   BX = Not Used         BX = Destroyed         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Destroyed         *
;   SI = Not Used         SI = Destroyed         *
;   DI = Not Used         DI = Destroyed         *
;                                                                       *
;***********************************************************************/
   even

ReStart586   proc

   StatisticsUpdate AdapterReset1      ; Update counters.

   call   Initialize586
   jne   ReStart586         ; Reset if error.
   ret               ; Exit.

ReStart586   endp



DummyRCBError:
   StatisticsUpdate DummyRCBCount      ; Update counter.

PollRestart586:
   call   ReStart586         ; Attempt to restart it.
   jmp   short CheckDeadMan      ; Continue polling.

;***********************************************************************\
;                                                                       *
; Poll586 - Read SCB Status to check for transmit or receive interrupt.   *
; If no interrupts from 82586, process deadman timer.         *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Destroyed         *
;   BX = Not Used         BX = Destroyed         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Destroyed         *
;   SI = Not Used         SI = Destroyed         *
;   DI = Not Used         DI = Destroyed         *
;                                                                       *
;***********************************************************************/
   even

Poll586      proc

   mov   cx, es            ; Set loop counter (Init to 0).
WaitForCommandEmpty:
   cmp   ds: SCB.SCBCommand, 0      ; Command finished.
   loopne   WaitForCommandEmpty      ; Loop if not.
   jne   PollRestart586         ; 586 might be hosed.

;---------------------------------------------------------------\
;                                                               *
; When a large received frame (3200 bytes, etc.) is too big for   *
; our buffer to hold, the 82586 incorrectly uses the DUMMY_RCB   *
; even though its size field is zero. This results in the   *
; DUMMY_BUFFER having 8 bytes stored into it and the the 82586   *
; updates the DUMMY_RCB with a new size of 8008h (EL is set).   *
; Also the status field (where the ActualCount is) is updated   *
; to a value of 8000h. So the 586 set the EOF bit but the F bit   *
; is still clear. So a test for 4000h likely does nothing.   *
;                        *
;---------------------------------------------------------------/

   test   ds:DUMMY_RCB.BDStatus, 4000h   ; Was Dummy RCB used?
   jne   DummyRCBError         ; Jump if so.

   mov   ax, ds: SCB.SCBStatus
   mov   bx, ax
   and   ax, 0F000h         ; Any interrupts(CX,FR,CNA,RNR)?
   jz   CheckOutOfResources      ; Jump if None.

   mov   ds: SCB.SCBCommand, ax      ; Ack all interrupts that were set.
   mov   DeadManHigh, 35         ; Set Max deadman.

   mov   dx, CA_PORT         ; Tug on the 82586's
   out   dx, al            ;  channel attention.

   mov   cx, es            ; Set loop counter (Init to 0).
WaitForAcknowledge:
   cmp   ds: SCB.SCBCommand, 0      ; Is command finished?
   loopne   WaitForAcknowledge      ; Loop if not.
   jne   PollRestart586         ; 586 might be hosed.

   test   bx, FR_INTERRUPT      ; Was a frame received?
   jz   short NoFrame         ; Jump if not.

   push   bx            ; Save SCB status.
   call   ProcessFRInterrupt      ; Process Rx Interrupt.
   pop   bx
   jnz   PollRestart586         ; Jump if RUStart failed.

NoFrame:
   test   bx, CX_INTERRUPT      ; Transmit complete?
   jz   short CheckDeadMan      ; Jump if not.

   call   ProcessCXInterrupt      ; Process Tx Interrupt.

CheckDeadMan:

   dec   DeadMan            ; Decrement DeadManLow.
   jz   DecDeadManHigh         ; Jump if zeroed.
   ret               ; Exit.

DecDeadManHigh:
   dec   DeadManHigh         ; Decrement DeadManHigh.
   jz   DeadManExpired         ; Jump if zeroed.
   ret               ; Exit.

DeadManExpired:
   jmp   ReStart586         ; Re-start the 82586.
   ;ret

CheckOutOfResources:
   mov   ax, ds: SCB.SCBStatus
   and   al, 70h
   cmp   al, 40h            ; Is RU still in Ready State?
   je   CheckDeadMan         ; Jump if yes.

   jmp   ReStart586         ; Start recovery

Poll586      endp

TxSingleCollision:
   StatisticsUpdate TxOKSingleCollision   ; Update stat counters.
   jmp   short ErrorReEntry


;***********************************************************************\
;                                                                       *
; ProcessCXInterrupt - Reset transmit deadman counters. If transmit   *
; successful, move it onto free list and exit. If an transmit error   *
; occurred, set statistic counter and retry transmission if the retry   *
; counter decremented is non-zero.               *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Destroyed         *
;   BX = Not Used         BX = Destroyed         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Destroyed         *
;   SI = Not Used         SI = Destroyed         *
;   DI = Not Used         DI = Destroyed         *
;                                                                       *
;***********************************************************************/
   even

ProcessCXInterrupt   proc

IF DEBUG
   mov   al, 'C'
   call   OutChar
ENDIF
   mov   bx, TxCHead         ; BX -> 1st Tx command block.
   or   bx, bx            ; Empty list?
   jz   IgnoreCX         ; Jump if so.
   mov   ax, [bx].TxStatus      ; AX = Tx Status.
   test   ah, HIGH TX_BUSY      ; Transmit still busy?
   jnz   IgnoreCX         ; Jump if so.
   test   ah, HIGH TX_OK         ; Transmit successful?
   jz   TxError            ; Jump if not.
   xor   ah, ah            ; clear high byte.
   and   al, 0fh            ; AX = Number of collisions.
   cmp   al, 1            ; Single collision?
   je   TxSingleCollision      ; Jump if so.
   add   word ptr StatisticCounters.TxOKMultipleCollisions, ax
   mov   ax, es            ; AX = 0.
   adc   word ptr StatisticCounters.TxOKMultipleCollisions+2, ax

ErrorReEntry:
   mov   di, [bx].TxCLink      ; DI -> Next Tx command block.
   or   di, di            ; End of list?
   jnz   StartNextCommand      ; Jump if not.

   mov   TxCEnd, di         ; Clear Transmit queue
   mov   TxCHead, di         ; pointers.

FreeTxBuff:
   mov   ax, TxFreeHead         ; AX -> old Free Head.
   mov   TxFreeHead, bx         ; Free Head = BX.
   mov   [bx].TxCLink, ax      ; Link Old to New.

IgnoreCX:
   ret


;***************************************************************\
;                                                               *
; Another transmit is on the list. Start it.         *
;                                                               *
;***************************************************************/
;
StartNextCommand:
   mov   DeadManHigh, 5         ; Set Tx Deadman.
   xchg   di, bx            ; DI -> old, BX -> next.
   mov   TxCHead, bx         ; Head = BX.
   mov   ax, 100h         ; AX = SCB command.
   call   Command586NoRestart      ; Issue Start Transmit.
   jnz   StartNextError         ; Jump if 586 locked.
   mov   bx, di            ; BX -> old Tx command block.
   jmp   FreeTxBuff         ; Add it to the free list.
StartNextError:
   jmp   ReStart586

;---------------------------------------------------------------\
;                                                               *
; Transmit error paths.                  *
;                                                               *
;---------------------------------------------------------------/
;
TxError:
   test   ah, HIGH CARRIER_SENSE_ERROR   ; Carrier sense loss error?
   jz   TxErrorCheckClearToSend      ; Jump if not.
   StatisticsUpdate TxAbortCarrierSense   ; Update counters.
   StatisticsUpdate PacketTxMiscErrorCount
   jmp   short TxErrorCheckCollisions   ; Attempt a retry.
TxErrorCheckClearToSend:
   test   ah, HIGH CLEAR_TO_SEND_ERROR   ; Clear to send error?
   jz   TxErrorCheckUnderRun      ; Jump if not.
   StatisticsUpdate TxClearToSend      ; Update counters.
   StatisticsUpdate PacketTxMiscErrorCount
   jmp   short TxErrorCheckCollisions   ; Attempt a retry.
TxErrorCheckUnderRun:
   test   ah, HIGH UNDERRUN_ERROR      ; Under runs?
   jz   TxErrorCheckTxDefers      ; Jump if not.
   StatisticsUpdate TxUnderRun      ; Update counters.
   StatisticsUpdate PacketTxMiscErrorCount
   jmp   short TxErrorCheckCollisions   ; Attempt a retry.
TxErrorCheckTxDefers:
   test   al, LOW TX_DEFERED_ERROR   ; Tx defer error?
   jz   TXErrorCheckMaxCollisions   ; Jump if not.
   StatisticsUpdate TxOKButDeferred   ; Update counters.
   jmp   short TxErrorCheckCollisions   ; Attempt a retry.
TXErrorCheckMaxCollisions:
   test   al, LOW MAX_COLLISION_ERROR   ; Max collisions error?
   jz   TxErrorCheckCollisions      ; Jump it not.
   StatisticsUpdate TxAbortExcessCollisions  ; Update counters.
   StatisticsUpdate PacketTxMiscErrorCount

TxErrorCheckCollisions:
   xor   ah, ah            ; Clear high byte.
   and   al, 0fh            ; AX = Number of collisions.
   cmp   al, 1            ; Single collision?
   je   TxErrorSingleCollision      ; Jump if so.
   add   word ptr StatisticCounters.TxOKMultipleCollisions, ax
   mov   ax, es            ; AX = 0.
   adc   word ptr StatisticCounters.TxOKMultipleCollisions+2, ax
   jmp   short TxErrorCheckRetries

TxErrorSingleCollision:
   StatisticsUpdate TxOKSingleCollision   ; Update stat counters.

TxErrorCheckRetries:
   dec   [bx].TxTries         ; Decrement retry counter.
   jnz   TransNeedsRetry         ; Jump if we can retry.

   StatisticsUpdate TxRetryFailure      ; Update counters.
   jmp   ErrorReEntry         ; Abort this packet.

TransNeedsRetry:
   mov   DeadManHigh, 5         ; Set Tx Deadman.
   mov   [bx].TxStatus, 0      ; Clear status field.
   mov   ax, 100h         ; AX = command start.
   call   Command586         ; Start transmit.

   StatisticsUpdate RetryTxCount      ; Update counters.

   ret
ProcessCXInterrupt   endp


;***********************************************************************\
;                                                                       *
; Misc. routines used by ProcessFRInterrupt (frame reception)      *
;                           *
;***********************************************************************/


IFNDEF NE3200P
if SupportJabber
FixNoRBDCase:
   lea   di, [si].DataBuffer      ; DI -> Data buffer.
   mov   bp, (RBuffSize-14)
   cmp   PromiscuousMode, 0      ; Promiscuous mode on?
   je   FixNoRBDNotProm         ; Jump if not.
   mov   bp, (MAX_JABBER-14)      ; BP = RBDBufSize.
FixNoRBDNotProm:
   add   di, bp            ; DI -> Right RBD.
   mov   [si].URBDOffset, di      ; Fix FD to point to empty RBD.
   mov   bp, 0c000h         ; Set EOF(End Of Frm) & F, but
   mov   [di+0], bp         ;  clear ActualCount(zero size).
   jmp   short FixNoRBDCaseReturn
endif
ENDIF

ReceiveAllTooBig:
   StatisticsUpdate   PacketRxTooBigCount
   or      ax, 100h         ; Set ASPEC TooBig err Status.
;;   or      ax, 10h            ; Set CSPEC TooBig error Status.
   call    CheckForDeformedPacket      ; Check if pkt also malformed.
   jmp   ReceiveCheckTooBig      ; Continue processing.

ReceiveAllPackets:
   test   ah, 3            ; Rx Overrun/Frame Overflow?
   jne   TooLargeAPacket         ; Jump if so (Bits S8/S9 set).

IFNDEF NE3200P
if SupportJabber
   mov   di, [si].URBDOffset
   cmp   di, -1            ; 14 byte packet?
   je   FixNoRBDCase         ; Jump if so.
   mov   bp, [di+0]         ; CX= EOF/F/ActualCount of RBD.
FixNoRBDCaseReturn:

else
   mov   bp, [si].RBDStatus      ; CX= EOF/F/ActualCount of RBD.
endif

   and   bp, 3FFFh         ; Mask off status to get size.
   add   bp, 14            ; Add media header size.
   mov   RcvFrameSize, bp      ; Save for CopyPacketToHost.

ENDIF
   mov   bx, ax            ; Get frm status reported by 586.

ReceiveAllCheckAlignCRC:
   and   ax, 0400h         ; Extract Alignment Err bit
   shr   ax, 9            ; Position Align per ASPEC

   and   bx, 0800h         ; Extract CRC Err bit
   shr   bx, 11            ; Position CRC per ASPEC
   or   ax, bx            ; AX= Initialize the Status.

ReceiveAllCheckShort:
   cmp   bp, 60            ; Runt? (64- 4 FCS bytes = 60).
   jae   ReceiveAllCheckBig      ; Jump if not.
   or   al, 04h            ; Set Runt Packet Error.
ReceiveAllCheckBig:
   cmp   bp, 1514         ; Too Big?
   jbe     ReceiveCheckTooBig      ; Jump if No (Continue processing).

   jmp   short ReceiveAllTooBig      ; Frame is too big (Set err bit).


ErrorPacketSlide:
   StatisticsUpdate PacketSlideCount
            ; 586 accepted a frame where DestAdr(DA) isn't
   jmp   ReStart586   ; a multicast nor does the DA match our IA.


PacketOverMax:
   StatisticsUpdate   PacketRxTooBigCount
   jmp   ReStart586

TooLargeAPacket:
   jmp   short ReLinkFD

TooShort:
   StatisticsUpdate   PacketRxTooSmallCount   ; Update counters.
   StatisticsUpdate   PacketRxMiscErrorCount

   jmp   short ReLinkFD

;***********************************************************************\
;                                                                       *
; ProcessFRInterrupt - If frame was received successfully, call      *
; CopyPacketToHost routine to transfer to the host HSM.         *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Destroyed         *
;   BX = Not Used         BX = Destroyed         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Destroyed         *
;   SI = Not Used         SI = Destroyed         *
;   DI = Not Used         DI = Destroyed         *
;                                                                       *
;***********************************************************************/
   even

ProcessFRInterrupt   proc

IF DEBUG
   mov   al, 'R'
   call   OutChar
ENDIF
   mov   si, FDHead         ; SI -> Next Rx Frame.
   mov   ax, [si].UFDStatus      ; AX = Frame Status.
   test   ah, 80h            ; Frame Complete ?
   jz   short PFRIntExit      ; Jump if no.

ReceiveLoop:

IFNDEF NE3200P

   cmp   PromiscuousMode, 0
   jne   ReceiveAllPackets      ; Jump if in promiscuous.

   call   CheckPacketSlide      ; Is frame Broad/MultiCast or
                  ;  matches our IA (node adr)?
   jne   ErrorPacketSlide      ; Restart the 586 (hardware err).

ELSE
   call   ProcessRBDLenCopyFrameHdr   ; BP = Packet size (Actual-
                  ;  counts + hdr size of 14).
   cmp     PromiscuousMode, 0
   jne     ReceiveAllPackets               ; Jump if in promiscuous.

ENDIF

   cmp   ax, 0A000h         ; Frame received OK ?
   jne   ReLinkFD         ; Jump if not.

ReceiveOK:
   mov   ax, es            ; AX = 0 (Init to good status).

IFNDEF NE3200P
if SupportJabber
   mov   di, [si].URBDOffset
   mov   bp, [di+0]         ; BP= EOF/F/ActualCount of RBD.
else
   mov   bp, [si].RBDStatus      ; BP= EOF/F/ActualCount of RBD.
endif
   and   bp, 3FFFh         ; Mask off the EOF/F bits.
   add   bp, 14            ; Add media hdr for total
                  ;  byte size of frame.
   mov   RcvFrameSize, bp      ; Save for CopyPacketToHost.
ENDIF

   cmp     bp, 60                     ; Frm too short? (64-4 FCS bytes).
IF DEBUG
   jae   NotTooShort
   jmp   TooShort
NotTooShort:
ELSE
   jb   TooShort                   ; Jump if so.
ENDIF

   cmp   bp, 1514
   ja   PacketOverMax

ReceiveCheckTooBig:            ; AX = Frame error status

IFNDEF NE3200P               ; Ne3200P already tested this.
   cmp   bp, MaxReceivePacketLength   ; Over max allowed by the OS?
   ja   ReLinkFD; TooLargeAPacket   ; Jump if so (ReLink the FD).

;-----------------------------------------------------------------------\
;                           *
; During frame reception the 82586 copies the frame header into the   *
; Frame Descriptor (FD) starting at UniReceiveBuf.UFDDestination. The   *
; HSM driver requires the header to precede frame data (info field).   *
; Therefore we copy the header ahead of the frame's info field.      *
;                           *
;-----------------------------------------------------------------------/

   lea   di, [si].UHeader      ;
   lea   si, [si].UFDDestination      ; SI -> Media header in FDesc.
   mov   cx, 14 / 2         ; Copy 14 bytes of frame hdr.
 rep   movsw               ; Movsw increments SI to point
                  ;  to UniReceiveBuf.URCB.

   mov   word ptr [si].ARDriverWorkSpace, es  ; Assume Non 802.2 (2nd byte=0).

;-----------------------------------------------------------------------\
;                           *
; On the host (see DriverISR procedure), ARESREBXValue is the first   *
; dword of RProtocolWorkspace where the frame's error status is saved.   *
; The second dword of RProtocolWorkspace is the frame's byte length.   *
;                           *
;-----------------------------------------------------------------------/

   mov   word ptr [si].ARESREBXValue, ax  ; RProtocolWorkspace's 1st
   mov   word ptr [si].ARESREBXValue+2, es ;  dword (error status).
   mov   [si].ARSocket, bp       ; RProtocolWorkspace's 2nd
   mov   [si].ARProtocolWorkspaceRest, es ;  dword (frame size).

ELSE   ; NE3200Promisc. code
   or    word ptr RCB_TEMPLATE.ARESREBXValue, ax ; Save any error status.
                  ; (Other values already setup).
ENDIF

   cmp   NextEmptyRCB+2, -1      ; Hi adr of host ECB (RCB) -1?
   je   CheckHostForRCB         ; Jump if yes (We need an RCB!).

ReceiveECBsOK:

IFNDEF NE3200P

   mov   bx, si            ; BX -> RCB structure template.
                  ; BP = Frame size (AX=Err Status).
   call   CopyPacketToHost      ; Setup to copy packet to host.

ReLinkFD:
   mov   si, FDHead         ; SI ->Just processed Rx Frame.
;;;;;
;;if SupportJabber
;   mov   ax, (RBuffSize-14) or 8000h   ; Setup Non-jabber buff size.
;   cmp   PromiscuousMode, 0      ; Promiscuous mode on?
;   je   ReLinkSizeNotProm      ; Jump if not.
;
;   mov   ax, (MAX_JABBER-14) or 8000h   ; AX = RBDBufSize (jabber).
;
;ReLinkSizeNotProm:
;   mov   di, [si].URBDOffset      ; Setup data buffer size.
;   mov   [si+8], ax         ;;; Should be di not si.
;else
;   mov   [si].RBDBufSize, RBuffSize or 8000h   ; Set Size/EL.
;endif


ELSE   ; Ne3200Promisc code
                  ; BP = Frame size (AX=Err Status).
   lea     bx, RCB_TEMPLATE      ; BX -> RCB structure template.

   call    CopyPacketToHost                ; Setup to copy packet to host.

ReLinkFD:
   cmp   RBDTotalSize, 14      ; Very small runt with no RBD?
   jbe   short LinkRBDDone      ; Jump if Yes.

   mov     si, RBDEnd         ; Get last RBD of previous rcv frm.
   lea     di, [si + size RBDStruct]       ; DI->1st RBD of just processed frm.
   cmp     di, BufferHead                  ; Past the last RBD? (Do we
                  ;  need to wrap the RBD ptr?).
   jb      LinkToNextRBD                   ; Jump if not.
   mov     di, RBDHead                     ; DI -> Start of RBD buffers.
LinkToNextRBD:
   mov     [si].RBDLink, di                ; Link last RBD of previous rcv
                  ;  frm to 1st RBD of just processed.

   mov     [si].RBDBufSize, 256            ; Clear EL bit in previous RCB.
   mov     si, RBDLast         ; Last RBD of just processed frm.
   mov     RBDEnd, si                      ; RBDEnd->New last RBD of
                  ;  previous rcv frame.
LinkRBDDone:
   mov     si, FDHead                      ; SI->FD of just processed frm.
   mov     [si].UFDRBDOffset, -1           ; Indicate no RBD is linked.
ENDIF

   mov   [si].UFDCommand, 8000h      ; Just processed frm is the
                  ;  new EL (End of List).
   mov   [si].UFDStatus, es      ; Zero status of just processed.
   mov   bx, FDEnd         ; BX -> Get old last frame.
   mov   [bx].UFDCommand, es      ; Zero EL bit in old last frm.
   mov   FDEnd, si         ; FDEnd-> Just processed frame.
   mov   si, [si].UFDLink      ; SI -> Next new frame rec'd.
   mov   FDHead, si         ; Save new rcv frame list head.
   mov   ax, [si].UFDStatus      ; AX = Status of new rcv frame.
   test   ah, 80h            ; New frame completely rec'd?
   jnz   ReceiveLoop         ; Jmp if so (Process new frm).

PFRIntExit:
   ;
   ; Fall thru to RUStart.
   ;

ProcessFRInterrupt   endp


;***********************************************************************\
;                                                                       *
; RUStart - Start the Receive Unit. We only attempt to start the RU if   *
;       it is Not already in the READY state.         *
;                           *
;***********************************************************************<
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Destroyed         *
;   BX = Not Used         BX = Destroyed         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Destroyed         *
;    BP = Not Used         BP = Preserved         *
;   SI = Not Used         SI = Preserved         *
;   DI = Not Used         DI = Preserved         *
;                           *
; On Exit: If the RU was started okay or if it was already in the   *
;          READY state then the ZeroFlag (ZF) = 1 indicating success.   *
;                                                                       *
;***********************************************************************/
   even

RUStart      proc

   mov   ax, ds:SCB.SCBStatus      ; AX = SCB Status.
   and   al, 70h            ; Extract the RUStatus bits.
   cmp   al, 40h            ; Is RecUnit already Ready?
   je   RUStartExit         ; Jmp if So (We're done).

   mov   bx, FDHead         ; BX -> First frame.
   mov   cx, es            ; Set loop counter (Init to 0).
RUWaitForCommand:
   cmp   ds: SCB.SCBCommand, 0      ; Previous command finished?
   loopne   RUWaitForCommand      ; Loop if not (CX !=0 & ZF=0).
   jne   RUStartExit         ; 586 might be hosed.

   mov   ds: SCB.RFAOffset, bx      ; Set RFA offset.
   mov   ds: SCB.SCBCommand, 10h      ; Start RU command.

   mov   dx, CA_PORT         ; Tug on the 82586's
   out   dx, al            ;  channel attention.

RUStartExit:
   ret               ; Exit

RUStart      endp


;***********************************************************************\
;                           *
; CheckHostForRCB                     *
;                           *
;***********************************************************************<

CheckHostForRCB:
                  ; BP= Frame size (Don't use it).
   mov   dx, BMIC_INDEX         ; We need more RCBs from
   mov   al, PEEK_POKE_ADDR OR AUTO_INC   ;  the host or we discard
   out   dx, al            ;  this frame.
   mov   dx, BMIC_DATA         ;

   mov   bx, RCBListPointer + 2      ; BX -> host RCB List.
   mov   ax, RCBIndex         ; AX = Index into list.
   shl   ax, 2            ; AX = Index * 4.
   add   ax, RCBListPointer      ; Add to base of list.
   adc   bx, 0            ; BX:AX -> Next RCB slot.

   out   dx, al            ; Set addr 0.
   mov   al, ah            ;
   out   dx, al            ; Set addr 1.
   mov   ax, bx            ;
   out   dx, al            ; Set addr 2.
   mov   al, ah            ;
   out   dx, al            ; Set addr 3.
   mov   dx, BMIC_INDEX         ; Set index to peek poke
   mov   al, PEEK_POKE_CONTROL      ;  control register.
   out   dx, al            ;
   mov   dx, BMIC_DATA         ; DX = BMIC Data Port.
   mov   al, 01011111b         ; Set Peek and
   out   dx, al            ;  start it.
   mov   dx, BMIC_STATUS         ; DX = BMIC Status/Control.
WaitForPeek1:               ;
   in   al, dx            ; Read BMIC status.
   test   al, PEEK_POKE_PENDING      ; Peek/Poke pending?
   jnz   WaitForPeek1         ; Jump if so.

   mov   al, PEEK_POKE_DATA OR AUTO_INC   ; Set index to Peek Poke
   mov   dx, BMIC_INDEX         ;  data port.
   out   dx, al
   mov   dx, BMIC_DATA         ; DX = BMIC Data port.
   mov   di, offset NextEmptyRCB      ; DI -> Local buffer.

   mov   cx, 4            ;
 rep   insb               ; Read four bytes of RCB adr.

   cmp   NextEmptyRCB+2, -1      ; Does host have a valid RCB
                  ; for us to copy rcv frame to?
   je   NoRCBAvailable         ; Jump if No
   jmp   ReceiveECBsOK         ; Jump if Yes.

NoRCBAvailable:
   InterruptTheHost         ; Still need an RCB.

IFDEF NE3200P
   StatisticsUpdate PacketRxOverflowCount
   StatisticsUpdate PacketRxMiscErrorCount
ENDIF
   jmp   ReLinkFD         ; Discard this frame and exit.


IFNDEF NE3200P
;***********************************************************************\
;                                                                       *
; CheckPacketSlide - verify the 586 hardware correctly received a frm.   *
; This check is done due to an Errata condition.         *
;                           *
;***********************************************************************/
;                                                                       *
; On Entry:         On Exit:            *
;   AX = Not Used         AX = Unused         *
;   BX = Not Used         BX = Unused         *
;   CX = Not Used         CX = Destroyed         *
;   DX = Not Used         DX = Unused         *
;    BP = Not Used         BP = Unused         *
;   SI = @ UniReceiveBuf struct   SI = Preserved         *
;   DI = Not Used         DI = Unused         *
;                                                                       *
;***********************************************************************/
;                                                                       *
   even
CheckPacketSlide   proc
   test   [si].UFDDestination+0, 01      ; Group bit set?
   jne   ReceiveCheckOK            ; Jump if yes.
   mov   cx, word ptr [si].UFDDestination+0
   cmp   cx, word ptr BoardID+0
   jne   CheckSlideError
   mov   cx, word ptr [si].UFDDestination+2
   cmp   cx, word ptr BoardID+2
   jne   CheckSlideError
   mov   cx, word ptr [si].UFDDestination+4
   cmp   cx, word ptr BoardID+4
CheckSlideError:
   ret
ReceiveCheckOK:
   xor   cl, cl
   ret               ; return
CheckPacketSlide   endp

ENDIF


IFDEF NE3200P
;***********************************************************************\
;                                                                       *
; ProcessRBDLenCopyFrameHdr - Ne3200Promisc code.            *
; Follow the RBD links to add up the total frame size (ActualCounts).   *
; Note: RBD's point to data buffers of 256 bytes in size.      *
; Then check frame size and copy the frame hdr into the RCB template   *
; Setting up some of the RCB template helps out CopyPacketToHost   *
;                           *
;***********************************************************************/
;                                                                       *
   even
ProcessRBDLenCopyFrameHdr   proc

   mov     di, [si].UFDRBDOffset           ; DI -> First RBD.
   mov     bp, 14                          ; Start with size of MAC hdr.
   mov     RBDSizeBeforeWrap, 0
   mov     RBDSizeAfterWrap, 0
   mov     RBDWrappedFlag, 0
   cmp   di, -1            ; Did the FD use an RBD?
   je   ProcessRBDSmallRunt      ; Jump if NO (Very short runt).

ProcessRBDLoop:
   mov     dx, [di].RBDStatus              ; DX= EOF/F/ActualCount of RBD.
   and     dh, 3fh                         ; Remove EOF(End Of Frame) & F.
   add     bp, dx            ; BP = Total byes so far.

   cmp     RBDWrappedFlag, 0               ; Did data buffers wrap from
                  ;  last one to the 1st yet?
   jne     ProcessRBDWrapped               ; Jump if so.
   add     RBDSizeBeforeWrap, dx           ; Add to before wrap value.
ProcessRBDBackFromWrap:

   test    [di].RBDStatus, 8000h           ; End Of Frame (EOF)?
   jne     ProcessRBDFoundEOF              ; Jump if so.

   cmp     [di].RBDLink, di                ; Will RBD wrap next time?
   ja      ProcessRBDLink                  ; Jump if not
   mov     RBDWrappedFlag, 1               ; Set the wrapped flag.
   StatisticsUpdate RBDWrappedCount

ProcessRBDLink:
   mov     di, [di].RBDLink                ; DI -> Next RBD.
   jmp     ProcessRBDLoop                  ; Jump if not.

ProcessRBDWrapped:
   add     RBDSizeAfterWrap, dx            ; Add to after wrap value.
   jmp     ProcessRBDBackFromWrap

ProcessRBDFoundEOF:
   mov     RBDTotalSize, bp                ; Save total frame size.
   mov     [di].RBDBufSize, 256 OR 8000h   ; Set EL (End of List) bit.
   mov     [di].RBDLink, DUMMY_RCB
   mov     RBDLast, di                     ; Save for receive complete.

   cmp   bp, MaxReceivePacketLength   ; Over max allowed by the OS?
   ja   ProcessRBDOverMax      ; Jump if So (Ignore Frame).

ProcessRBDSetup:
   mov     di, [si].UFDRBDOffset           ; DI -> First RBD.
   mov     SaveRDBOffset, di               ; Save ptr to first RBD.
   lea     bx, RCB_TEMPLATE      ; BX -> RCB structure template.
   lea     si, [si].UFDDestination      ;
   lea     di, [bx+size AReceiveBufferStructure] ; DI -> RCVMACDest.
   mov     cx, 14 / 2                      ; Copy 14 bytes of hdr (DA/SA/len).
 rep   movsw

   mov   word ptr [bx].ARDriverWorkSpace + 1, es    ; Assume Non 802.2. frm.

   mov   word ptr [bx].ARESREBXValue, es       ; RProtocolWorkspace's 1st
   mov   word ptr [bx].ARESREBXValue+2, es   ;  dword (assume no err status).
   mov   [bx].ARSocket, bp          ; RProtocolWorkspace's 2nd
   mov   [bx].ARProtocolWorkspaceRest, es    ;  dword (frame size).

   ret               ; Exit - Continue processing.

ProcessRBDOverMax:
   jmp   ReLinkFD         ; Ignore frame -Relink the FD.

;---------------------------------------------------------------\
;                        *
; The 82586 does not accept frames that are smaller than 14   *
; bytes. So if the FD does not point to a RBD (i.e. -1) then   *
; the frame only had a DA, SA and a Length field (frm size =14).*
;                        *
;---------------------------------------------------------------/

ProcessRBDSmallRunt:
   mov     RBDTotalSize, bp                ; 14 = Save total Frame size.
   mov     [si].UFDRBDOffset, DUMMY_RCB   ; DI -> Dummy First RBD.
   jmp   short ProcessRBDSetup      ;

ProcessRBDLenCopyFrameHdr     endp

ENDIF



IF DEBUG
;
;   OutChar
;
;   AL = Debug Char
;
;   Register AX modified
;

   even

OutChar   proc

   push   si
   mov   si, CurDiagOffset
   mov   DiagBuffer[si], al
   mov   byte ptr DiagBuffer[si+1], '*'

   inc   si
   and   si, 0fh
   mov   CurDiagOffset, si
   jz   UpdateHostDebugBuffer
   pop   si
   ret

UpdateHostDebugBuffer:
   push   cx
   push   dx
   mov   si, offset DiagnosticSequence   ; SI -> Preset sequence.
   BMICTransferWithLocal
   pop   dx
   pop   cx

;;   add   word ptr StatisticCounters.RxAbortFrameAlignment, 1
;;   adc   word ptr StatisticCounters.RxAbortFrameAlignment+2, 0

   add   DiagnosticOffsetLo, 10h
   adc   DiagnosticOffsetHi, 0
   dec   HostDiagSegCount
   jz   ResetHostDiagOffset
   pop   si
   ret

ResetHostDiagOffset:
   mov   ax, DiagnosticBufferPointer
   mov   DiagnosticOffsetLo, ax
   mov   ax, DiagnosticBufferPointer+2
   mov   DiagnosticOffsetHi, ax
   mov   HostDiagSegCount, 1000h/10h
   pop   si
   ret

OutChar   endp


;
;   OutWord
;
;   Put a word value to the screen
;
;   AX = word value
;
   even

OutWord   proc

   push   cx
   push   dx

   mov   dx, ax

;   Now output the counter
   mov   cx, 0404h

PrintWordLoop:
   rol   dx, cl
   mov   al, dl
   and   al, 0Fh

   add   al, 90h         ; Convert hex digit to ascii
   daa
   adc   al, 40h
   daa

   call   OutChar
   dec   ch
   jnz   PrintWordLoop

   pop   dx
   pop   cx
   ret

OutWord   endp


ENDIF


CODE   ends

   end