home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / packetdrivers.tar.gz / pd.tar / src / ne2.asm < prev    next >
Assembly Source File  |  1995-06-25  |  30KB  |  1,255 lines

  1. ; Packet driver for Novell's NE/2
  2. ; Written by:
  3. ;    Eric Henderson
  4. ;    Brigham Young University
  5. ;    
  6. ; Based on the "generic" packet driver by Russell Nelson with help
  7. ; from the western digital pd by Russell Nelson.
  8. ; 80[123]86 processor support lifted from 3com driver by permission
  9. ;    from Russell Nelson
  10. ;
  11. ;   Portions (C) Copyright 1990 BYU
  12.  
  13. version    equ    5
  14.  
  15. .286
  16.     include    defs.asm
  17.  
  18. code    segment    byte public
  19.     assume    cs:code, ds:code
  20.  
  21. ;*****************************************************************************
  22. ;
  23. ;    NE/2 controller board offsets
  24. ;    IO port definition (BASE in io_addr)
  25. ;*****************************************************************************
  26. ADDROM  EQU    10h            ; LAN Address ROM
  27. RACK    EQU    10h            ; NE2 Port Window 
  28. NERESET EQU    20h            ; Issue a read for reset
  29.  
  30. ; 8390 LAN Controller (page0) register offset for read and write 
  31. CMDR    EQU    00h            ; command register for read & write
  32. CLDA0    EQU    01h            ; current local dma addr 0 for read
  33. PSTART    EQU    01h            ; page start register for write
  34. CLDA1    EQU    02h            ; current local dma addr 1 for read
  35. PSTOP    EQU    02h            ; page stop register for write
  36. BNRY    EQU    03h            ; boundary reg for rd and wr
  37. TSR    EQU    04h            ; tx status reg for rd
  38. TPSR    EQU    04h            ; tx start page start reg for wr    
  39. NCR    EQU    05h            ; number of collision reg for rd
  40. TBCR0    EQU    05h            ; tx byte count 0 reg for wr
  41. FIFO    EQU    06h            ; FIFO for rd
  42. TBCR1    EQU    06h            ; tx byte count 1 reg for wr
  43. ISR    EQU    07h            ; interrupt status reg for rd and wr
  44. CRDA0    EQU    08h            ; current remote dma address 0 for rd
  45. RSAR0    EQU    08h            ; remote start address reg 0  for wr
  46. CRDA1    EQU    09h            ; current remote dma address 1 for rd
  47. RSAR1    EQU    09h            ; remote start address reg 1 for wr
  48. RBCR0    EQU    0Ah            ; remote byte count reg 0 for wr
  49. RBCR1    EQU    0Bh            ; remote byte count reg 1 for wr
  50. RSR    EQU    0Ch            ; rx status reg for rd
  51. RCRWD    EQU    0Ch            ; rx configuration reg for wr
  52. CNTR0    EQU    0Dh            ; tally cnt 0 for frm alg err for rd
  53. TCR    EQU    0Dh            ; tx configuration reg for wr
  54. CNTR1    EQU    0Eh            ; tally cnt 1 for crc err for rd
  55. DCR    EQU    0Eh            ; data configuration reg for wr
  56. CNTR2    EQU    0Fh            ; tally cnt 2 for missed pkt for rd
  57. IMR    EQU    0Fh            ; interrupt mask reg for wr
  58. ; 8390 LAN Controller (page1) register offset for read and write 
  59. PAR0    EQU    01h             ; physical addr reg 0 for rd and wr
  60. PAR1    EQU    02h             ; physical addr reg 1 for rd and wr
  61. PAR2    EQU    03h             ; physical addr reg 2 for rd and wr
  62. PAR3    EQU    04h             ; physical addr reg 3 for rd and wr
  63. PAR4    EQU    05h             ; physical addr reg 4 for rd and wr
  64. PAR5    EQU    06h             ; physical addr reg 5 for rd and wr
  65. CURR    EQU    07h            ; current page reg for rd and wr
  66. MAR0    EQU    08h            ; multicast addr reg 0 fro rd and WR
  67. MAR1    EQU    09h            ; multicast addr reg 1 fro rd and WR
  68. MAR2    EQU    0Ah            ; multicast addr reg 2 fro rd and WR
  69. MAR3    EQU    0Bh            ; multicast addr reg 3 fro rd and WR
  70. MAR4    EQU    0Ch            ; multicast addr reg 4 fro rd and WR
  71. MAR5    EQU    0Dh            ; multicast addr reg 5 fro rd and WR
  72. MAR6    EQU    0Eh            ; multicast addr reg 6 fro rd and WR
  73. MAR7    EQU    0Fh            ; multicast addr reg 7 fro rd and WR
  74.  
  75. ;***********************************************************************
  76. ;
  77. ;    8003 control register operations
  78. ;***********************************************************************
  79.  
  80. MSK_RESET    EQU    80h            ; reset LAN controller
  81. MSK_ENASH    EQU    40h        ; enable PC access to shared mem
  82. MSK_DECOD    EQU    3Fh         ; ???? memory decode bits, corresponding
  83.                     ; to SA 18-13. SA 19 assumed to be 1
  84. ;***********************************************************************
  85. ;
  86. ;    8390 CMDR MASK
  87. ;***********************************************************************
  88.  
  89. MSK_STP        EQU    01h        ; software reset, take 8390 off line
  90. MSK_STA        EQU    02h        ; activate the 8390 NIC
  91. MSK_TXP        EQU    26h        ; initial txing of a frm  (With DMA)
  92. MSK_RD2        EQU    20h        ; abort remote DMA
  93. MSK_PG0        EQU    00h        ; select register page 0
  94. MSK_PG1        EQU    40h        ; select register page 1
  95. MSK_PG2        EQU    80h        ; select register page 2
  96. MSK_DMA_RD    EQU    0ah        ; start DMA read
  97. MSK_DMA_WR    EQU    12h        ; start DMA write
  98.  
  99. ;***********************************************************************
  100. ;
  101. ;    8390 ISR & IMR MASK
  102. ;***********************************************************************
  103.  
  104. MSK_PRX  EQU    01h        ; rx with no error
  105. MSK_PTX  EQU    02h        ; tx with no error
  106. MSK_RXE  EQU    04h        ; rx with error
  107. MSK_TXE  EQU    08h        ; tx with error
  108. MSK_OVW  EQU    10h        ; overwrite warning
  109. MSK_CNT  EQU    20h        ; MSB of one of the tally counters is set
  110. MSK_RDC  EQU    40h        ; remote dma completed
  111. MSK_RST     EQU    80h        ; reset state indicator
  112. MaskByte        equ    0
  113. UnmaskByte        equ    1fh
  114. InterruptMask        equ    0fh
  115.  
  116. ;***********************************************************************
  117. ;
  118. ;    8390 DCR MASK
  119. ;***********************************************************************
  120.  
  121. MSK_WTS EQU    01h        ; word transfer mode selection
  122. MSK_BOS    EQU    02h        ; byte order selection
  123. MSK_LAS    EQU    04h        ; long addr selection
  124. MSK_BMS    EQU    08h        ; burst mode selection
  125. MSK_ARM    EQU    10h        ; atuoinitialize remote
  126. MSK_FT00 EQU    00h        ; burst lrngth selection
  127. MSK_FT01 EQU    20h        ; burst lrngth selection
  128. MSK_FT10 EQU    40h        ; burst lrngth selection
  129. MSK_FT11 EQU    60h        ; burst lrngth selection
  130.  
  131. ;***********************************************************************
  132. ;
  133. ;    8390 RCR MASK
  134. ;***********************************************************************
  135.  
  136. MSK_SEP EQU    01h        ; save error pkts
  137. MSK_AR     EQU    02h        ; accept runt pkt
  138. MSK_AB     EQU    04h        ; accept broadcast 
  139. MSK_AM     EQU    08h        ; accept multicast 
  140. MSK_PRO    EQU    10h        ; promiscuous physical
  141.                 ; accept all pkt with physical adr
  142. MSK_MON EQU    20h        ; monitor mode
  143.  
  144. ;***********************************************************************
  145. ;
  146. ;    8390 TCR MASK
  147. ;***********************************************************************
  148.  
  149. MSK_CRC EQU    01h        ; inhibit CRC, do not append crc
  150. MSK_LB01 EQU    06h        ; encoded loopback control
  151. MSK_ATD    EQU    08h        ; auto tx disable
  152. MSK_OFST EQU    10h        ; collision offset enable 
  153.  
  154. ;***********************************************************************
  155. ;
  156. ;    8390 RSR MASK
  157. ;***********************************************************************
  158.  
  159. SMK_PRX  EQU    01h        ; rx without error
  160. SMK_CRC  EQU    02h        ; CRC error
  161. SMK_FAE  EQU    04h        ; frame alignment error
  162. SMK_FO   EQU    08h        ; FIFO overrun
  163. SMK_MPA  EQU    10h        ; missed pkt
  164. SMK_PHY  EQU    20h        ; physical/multicase address
  165. SMK_DIS  EQU    40h        ; receiver disable. set in monitor mode
  166. SMK_DEF     EQU    80h        ; deferring
  167.  
  168. ;***********************************************************************
  169. ;
  170. ;    8390 TSR MASK
  171. ;***********************************************************************
  172.  
  173. SMK_PTX  EQU    01h        ; tx without error
  174. SMK_DFR  EQU    02h        ; non deferred tx
  175. SMK_COL  EQU    04h        ; tx collided
  176. SMK_ABT  EQU    08h        ; tx aboort because of excessive collisions
  177. SMK_CRS  EQU    10h        ; carrier sense lost
  178. SMK_FU   EQU    20h        ; FIFO underrun
  179. SMK_CDH  EQU    40h        ; collision detect heartbeat
  180. SMK_OWC     EQU    80h        ; out of window collision
  181.  
  182. ;***********************************************************************
  183. ;
  184. ;    on board memory constant definition
  185. ;***********************************************************************
  186. ; for rcv buff ring of onboard mem
  187. START_PG EQU    46h                  ; start at page 46
  188. STOP_PG  EQU    80h            ; end at page 80 
  189. ; for tx buff of shr mem
  190. TB_SIZE EQU    1            ; number of tb buff in shr mem
  191. TB_PGNO EQU    6            ; number of pages in one tb buff
  192.  
  193. EIGHTBITSLOT    db    0        ;8-bit machine flag
  194. Path        dw    ?
  195. RxPath        dw    ?
  196.  
  197.     public    int_no, io_addr
  198. int_no        db    3,0,0,0        ;must be four bytes long for get_number.
  199. io_addr        dw    1000h,0        ; I/O address for card (jumpers)
  200. m_channel     dw     0        ; micro channel flag    
  201.  
  202. rd_NE    MACRO    port
  203.     mov    DX, CS:io_addr
  204.     add    DX, port        ; DX contains address of port
  205.     in    AL, DX            ; AL contains data read from port
  206.     ENDM
  207.  
  208. wr_NE    MACRO    port
  209.     mov    DX, CS:io_addr
  210.     add    DX, port        ; DX contains address of port
  211.     out    DX, AL            ; AL contains data to be written to port
  212.     ENDM
  213.  
  214. ReceiveHeaderStructure    struc
  215.    RReceiveStatus    db    ?
  216.    RNextBuffer        db    ?
  217.    RByteCount        dw    ?
  218.  
  219.    RDestinationAddress    db    6 dup(?)
  220.    RSourceAddress    db    6 dup(?)
  221.    RPacketLength    dw    ?
  222.    RChecksum        dw    ?
  223.    RRPacketLength    dw    ?
  224.    RTranControl        db    ?
  225.    RHPacketType        db    ?
  226.    RDestinationNet    db    4 dup(?)
  227.    RDestinationNode    db    6 dup(?)
  228.    RDestinationSocket    dw    ?
  229. ReceiveHeaderStructure    ends
  230.  
  231. ReceiveHeader    ReceiveHeaderStructure    <>
  232.  
  233. Current                equ    InterruptStatus
  234. CurrentDMA0            equ    RemoteStartAddress0
  235. CurrentDMA1            equ    RemoteStartAddress1
  236. ForRSTBit            equ    80h
  237.  
  238. NIC    struc
  239.     Command            db    ?
  240.     PageStart        db    ?
  241.     PageStop        db    ?
  242.     Boundry            db    ?
  243.     TransmitStatus        db    ?
  244.     TransmitByteCount0    db    ?
  245.     TransmitByteCount1    db    ?
  246.     InterruptStatus        db    ?
  247.     RemoteStartAddress0    db    ?
  248.     RemoteStartAddress1    db    ?
  249.     RemoteByteCount0    db    ?
  250.     RemoteByteCount1    db    ?
  251.     ReceiveConfiguration    db    ?
  252.     TransmitConfiguration    db    ?
  253.     DataConfiguration    db    ?
  254.     IntMask            db    ?
  255.     DataPort        db    ?
  256.                 db    15 dup (?)
  257.     Reset            db    ?
  258. NIC    ends
  259.  
  260. BLUEBOOK    equ    1
  261. IEEE8023    equ    11
  262.     public    driver_class, driver_type, driver_name, driver_function, parameter_list
  263. driver_class    db    BLUEBOOK, IEEE8023,  0    ;from the packet spec
  264. driver_type    dw    0ffffh        ;Wild card matches any type
  265. driver_name    db    'NE/2',0    ;name of the driver.
  266. driver_function    db    2
  267. parameter_list    label    byte
  268.     db    1    ;major rev of packet driver
  269.     db    9    ;minor rev of packet driver
  270.     db    14    ;length of parameter list
  271.     db    EADDR_LEN    ;length of MAC-layer address
  272.     dw    GIANT    ;MTU, including MAC headers
  273.     dw    MAX_MULTICAST * EADDR_LEN    ;buffer size of multicast addrs
  274.     dw    0    ;(# of back-to-back MTU rcvs) - 1
  275.     dw    0    ;(# of successive xmits) - 1
  276.     dw    0    ;Interrupt # to hook for post-EOI
  277.             ;processing, 0 == none,
  278.  
  279. mcast_list_bits db      0,0,0,0,0,0,0,0 ;Bit mask from last set_multicast_list
  280. mcast_all_flag  db      0               ;Non-zero if hware should have all
  281.  
  282.     public    rcv_modes
  283. rcv_modes    dw    7        ;number of receive modes in our table.
  284.         dw    0        ;there is no mode 1.
  285.         dw    rcv_mode_1
  286.         dw    rcv_mode_2
  287.         dw    rcv_mode_3
  288.         dw    rcv_mode_4
  289.         dw    rcv_mode_5
  290.         dw    rcv_mode_6
  291. rxcr_bits       db      MSK_AB        ; Default to ours plus multicast
  292.  
  293.  
  294.     public bad_command_intercept
  295. bad_command_intercept:
  296. ;called with ah=command, unknown to the skeleton.
  297. ;exit with nc if okay, cy, dh=error if not.
  298.     mov    dh,BAD_COMMAND
  299.     stc
  300.     ret
  301.  
  302.     public    as_send_pkt
  303. ; The Asynchronous Transmit Packet routine.
  304. ; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
  305. ;   interrupts possibly enabled.
  306. ; Exit with nc if ok, or else cy if error, dh set to error number.
  307. ;   es:di and interrupt enable flag preserved on exit.
  308. as_send_pkt:
  309.     ret
  310.  
  311.     public    drop_pkt
  312. ; Drop a packet from the queue.
  313. ; Enter with es:di -> iocb.
  314. drop_pkt:
  315.     assume    ds:nothing
  316.     ret
  317.  
  318.     public    xmit
  319. ; Process a transmit interrupt with the least possible latency to achieve
  320. ;   back-to-back packet transmissions.
  321. ; May only use ax and dx.
  322. xmit:
  323.     assume    ds:nothing
  324.     ret
  325.  
  326.  
  327.     public    send_pkt
  328. send_pkt:
  329. ;enter with ds:si -> packet, cx = packet length.
  330. ;exit with nc if ok, or else cy if error, dh set to error number.
  331.     assume    ds:nothing
  332. ; get txblk length                          
  333.     inc    cx
  334.     and    cl, 0feh
  335.     cmp    CX, RUNT
  336.     jnb    length_ok
  337.     mov    cx, RUNT
  338. length_ok:
  339.     cmp    cx, GIANT
  340.     jbe    length1_ok
  341.     mov    dh, NO_SPACE
  342.     stc    
  343.     jmp    count_out_err
  344. length1_ok:
  345. ;new stuff
  346.     mov    AX, CX
  347.     wr_NE    TBCR0            ; Transmit byte count
  348.     mov    al, ah
  349.     wr_NE    TBCR1
  350.     mov    al, 0
  351.     wr_NE    RSAR0
  352.     mov    al, 40h
  353.     wr_NE    RSAR1
  354.     mov    ax, cx
  355.     wr_NE    RBCR0            ; Remote byte count
  356.     mov    al, ah
  357.     wr_NE    RBCR1
  358.  
  359. ; Clear out DMA complete interrupt
  360.     mov    al, MSK_PG0        
  361.     wr_NE    CMDR
  362.     mov    al, 40h
  363.     wr_NE    ISR
  364.  
  365.     mov    al, MSK_DMA_WR
  366.     wr_NE    CMDR
  367.  
  368.     mov    DX, CS:io_addr
  369.     add    DX, RACK        ; DX has address NE/2 Port window 
  370.  
  371.     shr    cx, 1
  372.     rep    outsw
  373.  
  374.     xor    cx, cx            ; Prevent infinite loop
  375. WaitForDMAComplete:
  376.     rd_NE    ISR
  377.     test    al, 40h
  378.     jnz    DMAComplete
  379.     loop    WaitForDMAComplete
  380.  
  381. DMAComplete:
  382.     mov    al, MSK_TXP
  383.     wr_NE    CMDR
  384.     clc
  385. exit_now:
  386.     ret
  387.  
  388.  
  389.     public    set_address
  390. set_address:
  391. ;enter with ds:si -> Ethernet address, CX = length of address.
  392. ;exit with nc if okay, or cy, dh=error if any errors.
  393. ;This proc will set the first CX bytes of the ethernet address, leaving
  394. ; the rest unchanged.
  395.     assume    ds:nothing
  396.  
  397.     cmp    cx, 6
  398.     jbe    set1
  399.     mov    dh, BAD_ADDRESS
  400.     stc
  401.     ret
  402. set1:
  403.     mov    al, 61h
  404.     wr_NE    CMDR
  405.     mov    DX, CS:io_addr
  406.     add    DX, PAR0        ; DX has address 8390 Phys Address Reg
  407. AddressToChip1:
  408.     lodsb
  409.     out    dx, al
  410.     inc    dx
  411.     in    al,61h            ;wait a little while...
  412.     loop    AddressToChip1
  413.     pop    di
  414.     pop    es
  415.  
  416.     mov     al, 21h
  417.     wr_NE     CMDR
  418.     clc
  419.     ret
  420.  
  421. ; Routines to set address filtering modes in the DS8390
  422. ;    This was lifted from R. Clements' WD PD
  423. rcv_mode_1:     ; Turn off receiver
  424.     mov al,    MSK_MON      ; Set to monitor for counts but accept none
  425.     jmp short rcv_mode_set
  426. rcv_mode_2:     ; Receive only packets to this interface
  427.     mov al, 0               ; Set for only our packets
  428.     jmp short rcv_mode_set
  429. rcv_mode_3:     ; Mode 2 plus broadcast packets (This is the default)
  430.     mov al,    MSK_AB     ; Set four ours plus broadcasts
  431.     jmp short rcv_mode_set
  432. rcv_mode_4:     ; Mode 3 plus selected multicast packets
  433.     mov al,    MSK_AB+MSK_AM ; Ours, bcst, and filtered multicasts
  434.     mov     mcast_all_flag,0
  435.     jmp short rcv_mode_set
  436. rcv_mode_5:     ; Mode 3 plus ALL multicast packets
  437.     mov al,    MSK_AB+MSK_AM; Ours, bcst, and filtered multicasts
  438.     mov     mcast_all_flag,1
  439.     jmp short rcv_mode_set
  440. rcv_mode_6:     ; Receive all packets (Promiscuous physical plus all multi)
  441.     mov al,    MSK_AB+MSK_AM+MSK_PRO
  442.     mov     mcast_all_flag,1
  443. rcv_mode_set:
  444.     push    ax              ; Hold mode until masks are right
  445.     call    set_8390_multi  ; Set the multicast mask bits in chip
  446.     pop     ax
  447.     WR_NE    RCRWD
  448.     mov     rxcr_bits,al    ; Save a copy of what we set it to
  449.     ret
  450.  
  451.     public    set_multicast_list
  452. set_multicast_list:
  453. ;enter with ds:si ->list of multicast addresses, ax = number of addresses,
  454. ;  cx = number of bytes.
  455. ;return nc if we set all of them, or cy,dh=error if we didn't.
  456.     mov    dh,NO_MULTICAST
  457.     stc
  458.     ret
  459.  
  460. ; Set the multicast filter mask bits in case promiscuous rcv wanted
  461. ;    This was lifted from R. Clements' WD PD
  462. set_8390_multi:
  463.     
  464.     mov    al,MSK_RD2+MSK_PG1
  465.     WR_NE    CMDR 
  466.     mov    cx,8        ; Eight bytes of multicast filter
  467.     mov    si,offset mcast_list_bits  ; Where bits are, if not all ones
  468.     push    cs
  469.     pop     ds
  470.     cli            ; Protect from irq changing page bits
  471.  
  472.     mov    DX, CS:io_addr
  473.     add    DX, MAR0
  474.     mov    al,mcast_all_flag  ; Want all ones or just selected bits?
  475.     or    al,al
  476.     jz    set_mcast_2     ; z = just selected ones
  477.     mov    al,0ffh        ; Ones for filter
  478. set_mcast_all:
  479.     out    dx, al
  480.     inc    dl        ; Step to next one
  481.     jmp    $+2        ; limit chip access rate
  482.     loop    set_mcast_all
  483.     jmp short set_mcast_x
  484.  
  485. set_mcast_2:
  486.     lodsb                   ; Get a byte of mask bits
  487.     out    dx,al        ; Write a mask byte
  488.     inc    dl        ; Step to next I/O register
  489.     jmp    $+2        ; limit chip access rate
  490.     loop    set_mcast_2
  491. set_mcast_x:
  492.     mov    al, MSK_RD2+MSK_PG0
  493.     WR_NE    CMDR
  494.     sti            ; OK for interrupts now
  495.     ret
  496.  
  497.  
  498.     public    terminate
  499. terminate:
  500.     ret
  501.  
  502.  
  503.     public    reset_interface
  504. reset_interface:
  505. ;reset the interface.
  506.     assume    ds:code
  507.     mov    al, MSK_STP + MSK_RD2    
  508.     wr_NE    CMDR
  509.     mov al,    0ffh        ; Clear all pending interrupts
  510.     wr_NE    ISR
  511.     xor al,    al        ; Turn off all enables
  512.     wr_NE    IMR
  513.     wr_NE    NERESET        ; Hard reset NE/2
  514.     wr_NE    NERESET
  515.     wr_NE    NERESET
  516.     wr_NE    NERESET
  517.     wr_NE    NERESET
  518.     wr_NE    NERESET
  519.     wr_NE    NERESET
  520.     wr_NE    NERESET
  521.  
  522.     rd_NE    NERESET
  523.  
  524.     mov    al, 21h
  525.     wr_NE    CMDR
  526.     ret
  527.  
  528.  
  529. ;called when we want to determine what to do with a received packet.
  530. ;enter with cx = packet length, es:di -> packet type, dl = packet class.
  531. ;It returns with es:di = 0 if don't want this type or if no buffer available.    
  532.     extrn    recv_find: near
  533.  
  534. ;called after we have copied the packet into the buffer.
  535. ;enter with ds:si ->the packet, cx = length of the packet.
  536.     extrn    recv_copy: near
  537.  
  538. ;call this routine to schedule a subroutine that gets run after the
  539. ;recv_isr.  This is done by stuffing routine's address in place
  540. ;of the recv_isr iret's address.  This routine should push the flags when it
  541. ;is entered, and should jump to recv_exiting_exit to leave.
  542. ;enter with ax = address of routine to run.
  543.     extrn    schedule_exiting: near
  544.  
  545. ;recv_exiting jumps here to exit, after pushing the flags.
  546.     extrn    recv_exiting_exit: near
  547.  
  548.     extrn    count_in_err: near
  549.     extrn    count_out_err: near
  550.  
  551.  
  552.     public    recv
  553. recv:
  554. ;called from the recv isr.  All registers have been saved, and ds=cs.
  555. ;Actually, not just receive, but all interrupts come here.
  556. ;Upon exit, the interrupt will be acknowledged.
  557.     assume    ds:code
  558.  
  559. ; read irq status register
  560.     mov    al, MaskByte
  561.     wr_NE    IMR
  562.     sti
  563. rd_isr:
  564.     rd_NE    ISR            ; read isr into AL
  565.     and    AL, 3Fh            ; check bit0-bit5, if all 1's no irq
  566.     cmp    AL, 0            ; if any irq
  567.     jne    tst_ovw            ; some irq
  568.     cli
  569.     mov    al, UnmaskByte
  570.     wr_NE    IMR
  571.     ret                ; no more irq, exit
  572.  
  573. ; process OVW    (OVW = 1)       
  574. ;    may report error here
  575. tst_ovw:
  576.     test    AL, MSK_OVW        ; if OVW irq
  577.     jnz    prcs_ov            ; OVW (OVW = 1) 
  578.     jmp    test_rx            ; no OVW (OVW = 0) 
  579.  
  580. ; **************************************************************
  581. ; follow the DP8390 datasheet addendum to handle the buff ring overflow
  582. prcs_ov:
  583. ; 1. issue a STOP mode command
  584.     mov    AL, MSK_STP + MSK_RD2
  585.     wr_NE    CMDR
  586. ; 6. remove one packet from the ring
  587.     rd_NE    BNRY            ; BNRY in AL
  588.     add    AL, 1            ; start page of frm in AL
  589.     cmp    AL, STOP_PG        ; check boundary
  590.         jne    get1
  591.     mov    AL, START_PG        
  592. ; ring not empty
  593. get1:
  594.     mov    BH, AL            ; BX has the rx_frm pointer
  595.     mov    al, SIZE ReceiveHeader
  596.     wr_NE    RBCR0
  597.     xor    al, al
  598.     wr_NE    RBCR1
  599.     wr_NE    RSAR0
  600.     mov    al, bh
  601.     wr_NE    RSAR1
  602.     mov    al, MSK_DMA_RD
  603.     wr_NE    CMDR
  604.  
  605.     mov    DX, CS:io_addr
  606.     add    DX, RACK        ; DX has address NE/2 Port window 
  607.     mov    di, OFFSET ReceiveHeader
  608.     mov    ax, cs
  609.     mov    es, ax
  610.     mov    cx, SIZE ReceiveHeader
  611.     shr    cx, 1    
  612. Receive1:
  613. rep    insw
  614.  
  615. SkipReceive1:
  616.     mov    bx, offset ReceiveHeader
  617.     mov    AL, CS:[BX]        ; AL has the status byte
  618.     test    AL, SMK_PRX        ; if rx good
  619.     jz    fd_bnr            ; rx error, drop frm by forward bnry
  620.  
  621. ; good frm, call _rcv_frm
  622.     call     _rcv_frm           
  623.  
  624. fd_bnr:                    ;drop frm by forward BNRY
  625.     mov    al, cs:ReceiveHeader.RNextBuffer    ; al = next pointer 
  626.     sub    AL, 1            ; new BNRY in AL
  627.     cmp    AL, START_PG        ; check boundary
  628.     jae    wrbnr            ; unsigned arithmetic
  629.     mov    AL, STOP_PG - 1        ;
  630. ;    dec    AL            ;
  631. wrbnr:
  632.     wr_NE    BNRY
  633. ; 2. clear the remote byte count registers (RBCR0,RBCR1)
  634.     xor    AL, AL
  635.     wr_NE    RBCR0
  636.     wr_NE    RBCR1
  637. ; 3. poll the ISR for the RST bit
  638. plisr:
  639.     mov    cx, 0ffffh
  640.     rd_NE    ISR
  641.     test    AL, MSK_RST
  642.     jnz    plisr_ok
  643.     loop    plisr        ; keep polling until the RST bit set
  644. ; 4. place the NIC in loopback mode (mode 1 or 2) by writing 02 or 04 to TCR
  645. plisr_ok:
  646.     mov    AL, 02h        ; put it in mode 2 (internal loopback)
  647.     wr_NE    TCR
  648. ; 5. issue start mode command
  649.     mov    AL, MSK_STA + MSK_RD2
  650.     wr_NE    CMDR
  651. ; 7. out from loopback mode by writing 00 to TCR
  652.     xor    AL, AL
  653.     wr_NE    TCR        ; normal operation configuration
  654. ; clear OVW in ISR              
  655.     mov    AL, MSK_OVW 
  656.     wr_NE    ISR            ; clear OVW
  657.     call    count_in_err        ; increment overflow counter
  658.     jmp    rd_isr            ; back to the top
  659.  
  660.  
  661. ; end of the modification 
  662. ; *****************************************************
  663. ;    
  664. ;process PRX and RXE
  665. ;
  666. test_rx:        
  667.     test     AL, MSK_RXE        
  668.     jnz    prcs_rxe        ; RXE = 1
  669.     test      AL, MSK_PRX    
  670.     jnz    prcs_rx                 ; PRX = 1
  671.     jmp    test_tx
  672.  
  673. prcs_rxe:
  674.     call    count_in_err
  675.     mov    al, MSK_RXE
  676.     wr_NE    ISR
  677.     jmp    rd_isr
  678. prcs_rx:
  679.     mov    AL, MSK_PG1 + MSK_RD2    ; read CURR reg
  680.     wr_NE    CMDR
  681.     rd_NE    CURR
  682.     mov    BL, AL            ; CURR in BL 
  683.     mov    AL, MSK_PG0 + MSK_RD2    ; read BNRY reg
  684.     wr_NE  CMDR
  685.     rd_NE    BNRY            ; BNRY in AL
  686.     add    AL, 1            ; start page of frm in AL
  687.     cmp    AL, STOP_PG         ; check boundary
  688.     jne    go_cmp
  689.     mov    AL, START_PG         ;         
  690. go_cmp:
  691.     cmp    AL, BL            
  692.     jne    Ring_Not_Empty
  693.     jmp    rd_isr            ; buff ring empty
  694.  
  695. Ring_Not_Empty:
  696.     wr_NE    RSAR1
  697.     xor    al, al
  698.     wr_NE    RBCR1
  699.     wr_NE    RSAR0
  700.     mov    al, SIZE ReceiveHeader
  701.     wr_NE    RBCR0
  702.     mov    al, MSK_DMA_RD
  703.     wr_NE    CMDR
  704.  
  705.     mov    DX, CS:io_addr
  706.     add    DX, RACK        ; DX has address NE/2 Port window 
  707.     mov    di, OFFSET ReceiveHeader
  708.     mov    ax, cs
  709.     mov    es, ax
  710.     mov    cx, SIZE ReceiveHeader
  711.     shr    cx, 1
  712.  
  713. Receive2:
  714. rep    insw
  715.  
  716. SkipReceive2:
  717.     mov    bx, offset ReceiveHeader
  718.     mov    AL, CS:[BX]        ; AL has the status byte
  719.     test    AL, SMK_PRX        ; if rx good
  720.     jz    fd_bnry            ; rx error, drop frm by forward bnry
  721.  
  722. ; good frm, call _rcv_frm
  723.        call     _rcv_frm           
  724.  
  725. fd_bnry:                ; drop frm by forward BNRY
  726.     mov    al, CS:ReceiveHeader.RNextBuffer    ; al = next pointer 
  727.     sub    AL, 1            ; new BNRY in AL
  728.     cmp    AL, START_PG        ; check boundary
  729.     jae    wrbnry            ; unsigned arithmetic
  730.     mov    AL, STOP_PG - 1        ;
  731.  
  732. wrbnry:
  733.     wr_NE    BNRY
  734.     mov    al, MSK_PRX
  735.     wr_NE    ISR
  736.     jmp    prcs_rx     
  737.  
  738. ;process PTX and TXE
  739. test_tx:
  740.     test      AL, MSK_PTX    
  741.     jnz    prc_ptx                 ; PTX = 1
  742.     test     AL, MSK_TXE        
  743.     jnz    prc_txe            ; TXE = 1
  744.     jmp    test_cnt
  745.       
  746.  
  747. ; process tx good, update txok, lostcrs, collsn
  748. prc_ptx:                ; tx good 
  749.     rd_NE    TSR
  750.     test    AL, SMK_CRS        ; is crs set in TSR
  751.     jz    nocrs            ; no               
  752. nocrs:    
  753.     rd_NE    NCR            ; read number of collision in AL
  754.     mov    AL, MSK_PTX
  755.     wr_NE    ISR            ; clear PTX
  756.     jmp    rd_isr
  757.  
  758. ; process tx error, update .txbad .underrun
  759. prc_txe:                ; tx bad
  760.     call    count_out_err    
  761.     rd_NE    TSR
  762.     test    AL, SMK_FU        ; it fu set in TSR
  763.     jz    nofu            ; no               
  764. nofu:                                  
  765.         mov    AL, MSK_TXE
  766.     wr_NE    ISR            ; clear PTX
  767.     jmp    rd_isr
  768.  
  769. ; process counter overflow, update .algerr .crcerr .???(missed pkt)
  770. test_cnt:
  771.     test      AL, MSK_CNT    
  772.     jnz    prc_cnt            ; yes, process cnt
  773.     jmp    rd_isr                 ; no CNT irq, back to the top
  774. ; process CNT            
  775. prc_cnt:
  776.     mov    AL, MSK_CNT
  777.     wr_NE    ISR            ; clear CNT
  778.     jmp    rd_isr            ; back to the top
  779.  
  780. ; End of RECV
  781.  
  782. _rcv_frm:
  783.     
  784. ; read byte count 
  785.     mov     cx, cs:ReceiveHeader.RByteCount    ; Extract size of frame
  786.     cmp    CX, 5dch
  787.     jna    rxlen_ok
  788.     jmp    rcv_ret
  789. rxlen_ok:
  790.     sub    CX, 4            ; 4 control bytes
  791.     mov    AX, cs
  792.     mov    ES, AX
  793.     mov     di, offset cs:ReceiveHeader.RPacketLength
  794.     mov    dl, BLUEBOOK        ;default 
  795.     mov    ax, es:[di]
  796.     xchg    ah, al
  797.     cmp     ax, 1500
  798.     ja    BlueBookPacket
  799.     mov    di, offset cs:ReceiveHeader.RChecksum
  800.     mov    dl, IEEE8023
  801. BlueBookPacket:
  802.  
  803.     push    cx            ; Save frame size
  804.     push    es
  805.  
  806.     mov ax,    cs            ; Set ds = code
  807.     mov ds,    ax
  808.     assume    ds:code
  809.     call    recv_find        ; See if type and size are wanted
  810.                     ;     CX = packet length
  811.                     ;    ES:DI = packet type
  812.                     ;    ES:DI = packet type
  813.  
  814.     pop    ds            ; RX page pointer in ds now
  815.     assume    ds:nothing
  816.     pop    cx
  817.  
  818.     cld                ; Copies below are forward
  819.     mov ax,    es            ; Did recv_find give us a null pointer?
  820.     or  ax,    di            ; ..
  821.     je    no_buff            ; If null, don't copy the data    
  822. has_buf:  
  823.  
  824. ;Tell DMA to copy the whole packet for us
  825.     mov    ax, 4
  826.     wr_NE    RSAR0        ; Don't copy  4 8390 control bytes
  827.     mov    ax, cx        ; CX has byte count
  828.     wr_NE    RBCR0        ; LSB first
  829.     mov    al, ah
  830.     wr_NE    RBCR1        ; Now MSB
  831.  
  832.     mov    al, MSK_DMA_RD  ; Issue DMA read command
  833.     wr_NE    CMDR
  834.  
  835. ; copy from NE/2 on board memory using the window RACK
  836. ; use IN and stosb to do the copy, IN -> AX -> ES:DI (CX has byte count)                        
  837.  
  838. copynow:
  839.     push    cx        ; We will want the count and pointer
  840.     push    es        ;  to hand to client after copying,
  841.     push    di        ;  so save them at this point
  842.  
  843.     mov    DX, CS:io_addr
  844.     add    DX, RACK        ; DX has address NE/2 Port window (?)
  845.  
  846.     mov    si, cx
  847.     shr    cx, 1
  848.     rep    insw
  849.     test    si, 1
  850.     jz    call_rc
  851.     in    ax, dx
  852.     stosb
  853. call_rc:
  854.     pop    si            ; Recover pointer to destination
  855.     pop    ds            ; Tell client it's his source
  856.     pop    cx            ; And it's this long
  857.     assume    ds:nothing
  858.     call    recv_copy        ; Give it to him
  859.     jmp    short rcv_ret
  860.  
  861. ; no system buff availble to hold rx frm
  862. no_buff:
  863. rcv_ret:
  864.     movseg    ds,cs
  865.     assume    ds:code
  866.     ret
  867.  
  868.  
  869.     public    timer_isr
  870. timer_isr:
  871. ;if the first instruction is an iret, then the timer is not hooked
  872.     iret
  873.  
  874. ;any code after this will not be kept after initialization. Buffers
  875. ;used by the program, if any, are allocated from the memory between
  876. ;end_resident and end_free_mem.
  877.     public end_resident,end_free_mem
  878. end_resident    label    byte
  879. end_free_mem    label    byte
  880.  
  881.  
  882.     public    usage_msg
  883. usage_msg    db    "usage: NE2 [options] <packet_int_no>",CR,LF,'$'
  884.  
  885.     public    copyright_msg
  886. copyright_msg    db    "Packet driver for NE/2 version "
  887.         db    '0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,CR,LF
  888.         db    "Portions Copyright 1990, Eric Henderson, BYU",CR,LF,'$'
  889. int_no_name    db    "Interrupt number ",'$'
  890. io_addr_name    db    "I/O port ",'$'
  891. no_board_msg:
  892.     db    "NE/2 apparently not present at this IO address.",CR,LF,'$'
  893. HardwareFailure:
  894.     db    "The NE/2 is not responding.",CR,LF,'$'
  895.  
  896.     extrn    set_recv_isr: near
  897.  
  898.  
  899. ;enter with si -> argument string, di -> word to store.
  900. ;if there is no number, don't change the number.
  901. ;    extrn    decout: near
  902. ;    extrn    hexout: near
  903.  
  904. ;-> the assigned Ethernet address of the card.
  905.     extrn    rom_address: byte
  906.  
  907.     public    parse_args
  908. print_args:
  909.     mov    dx,offset int_no_name
  910.     mov    ah,9
  911.     int    21h
  912.     mov    di,offset int_no
  913.     mov    ax,[di]            ;print the number in hex.
  914.     mov    dx,[di+2]
  915. ;    call    hexout
  916.     mov    dl,' '
  917.     mov    ah, 2
  918.     int    21h
  919.     mov    dl,'('
  920.     mov    ah, 2
  921.     int    21h
  922.     mov    ax,[di]            ;print the number in decimal.
  923.     mov    dx,[di+2]
  924. ;    call    decout
  925.     mov    dl,')'
  926.     mov    ah, 2
  927.     int    21h
  928.     mov    dl,CR
  929.     mov    ah, 2
  930.     int    21h
  931.     mov    dl,LF
  932.     mov    ah, 2
  933.     int    21h
  934.     mov    dx,offset io_addr_name
  935.     mov    ah,9
  936.     int    21h
  937.     mov    di,offset io_addr
  938.     mov    ax,[di]            ;print the number in hex.
  939.     mov    dx,[di+2]
  940. ;    call    hexout
  941.     mov    dl,' '
  942.     mov    ah, 2
  943.     int    21h
  944.     mov    dl,'('
  945.     mov    ah, 2
  946.     int    21h
  947.     mov    ax,[di]            ;print the number in decimal.
  948.     mov    dx,[di+2]
  949. ;    call    decout
  950.     mov    dl,')'
  951.     mov    ah, 2
  952.     int    21h
  953.     mov    dl,CR
  954.     mov    ah, 2
  955.     int    21h
  956.     mov    dl,LF
  957.     mov    ah, 2
  958.     int    21h
  959. parse_args:
  960.     ret
  961.  
  962.     extrn    etopen_diagn: byte
  963.  
  964. BoardNotResponding:
  965.     mov    dx, offset HardwareFailure
  966.     mov    etopen_diagn, 35
  967.     jmp    short error_wrt
  968. bad_cksum:
  969. no_memory:
  970.     mov    dx,offset no_board_msg
  971.     mov    etopen_diagn,37
  972. error_wrt:
  973.     stc
  974.     ret
  975.  
  976. SlotSelectRegister        equ    96h
  977. POS0                equ    100h
  978. POS1                equ    101h
  979. POS2                equ    102h
  980. NE2ID                equ    7154h
  981.  
  982. IRQConfiguration        label    byte    ;possible IRQ settings
  983.                 db    3
  984.                 db    4
  985.                 db    5
  986.                 db    9
  987.  
  988. IOConfiguration          label    word    ;possible I/O Base addresses
  989.                 dw    1000h
  990.                 dw    2020h
  991.                 dw    8020h
  992.                 dw    0A0A0h
  993.                 dw    0B0B0h
  994.                 dw    0C0C0h
  995.                 dw    0C3D0h
  996.  
  997.     public    etopen
  998. etopen:
  999. ;if all is okay,
  1000.     cli                    ;Find the NE/2 NIC
  1001.     mov     cl, 7                ; channel position (8=1st slot
  1002.                         ; F=8th slot)
  1003. NextSlot:
  1004.     inc    cl
  1005.     cmp    cl,10h                ;check 8 I/O slots
  1006.     jne    NoHardwareFailure
  1007.     jmp    BoardNotResponding
  1008. NoHardwareFailure:
  1009.     mov    al, cl
  1010.     out    SlotSelectRegister, al        ;select card slot
  1011.     mov     dx, POS1
  1012.     in    al, dx                ;check for NE2 ID
  1013.     mov    ah, al
  1014.     mov    dx, POS0
  1015.     in    al, dx
  1016.     cmp    ax, NE2ID
  1017.     jne    NextSlot
  1018.  
  1019.     mov    dx, POS2            ;read configuration port
  1020.     in    al, dx                    
  1021.  
  1022.     test    al, 01                ;test to see if card enable
  1023.     jz    NextSlot            ; bit is set
  1024.  
  1025.     mov    bl, al
  1026.     and    bx, 000Eh            ;mask all but I/O base addr.
  1027.  
  1028.     jz    NextSlot            ;check next slot if no option
  1029.                         ; setting in POS 2
  1030.     dec    bx
  1031.     dec    bx
  1032.     mov    dx, IOConfiguration[bx]        ; IO address
  1033.     mov    io_addr, dx            ;save the base I/O address
  1034.  
  1035.     test    al, 10h                ;if boot prom enabled then
  1036.  
  1037. SetIRQ:
  1038.     shr    al, 5                ;load the hardware config.
  1039.     and    ax, 3                ; table with the IRQ line
  1040.     mov    bx, ax                ; value and string
  1041.     mov    dl, IRQConfiguration[bx]
  1042.     mov    int_no, dl            ;save interrupt level
  1043. ;    call    print_args
  1044.  
  1045.     call    set_recv_isr    ; Put ourselves in interrupt chain
  1046.  
  1047.     ; reset NE/2 board 
  1048.     cli
  1049.     wr_NE    NERESET
  1050.     wr_NE    NERESET
  1051.     wr_NE    NERESET
  1052.     wr_NE    NERESET
  1053.     wr_NE    NERESET
  1054.     wr_NE    NERESET
  1055.     wr_NE    NERESET
  1056.     wr_NE    NERESET
  1057.  
  1058.     rd_NE    NERESET
  1059.  
  1060.     mov    al, 21h
  1061.     wr_NE    CMDR
  1062.  
  1063.     ; Test to see if we're OK
  1064.     rd_NE    CMDR
  1065.     cmp    al, 21h
  1066.     je    WeBeOK
  1067.     jmp    BoardNotResponding
  1068.  
  1069. WeBeOK:
  1070.     mov    al, 49h            ;Word mode
  1071.     wr_NE    DCR        
  1072.     mov    al, 0
  1073.     wr_NE    RBCR0
  1074.     wr_NE    RBCR1
  1075.     mov    al, 4
  1076.     wr_NE    RCRWD
  1077.     mov    al, 02
  1078.     wr_NE    TCR        
  1079.     mov    al, START_PG
  1080.     wr_NE    PSTART        
  1081.     wr_NE    BNRY        
  1082.     mov    al, STOP_PG
  1083.     wr_NE    PSTOP        
  1084.  
  1085.     mov    al, 0ffh
  1086.     wr_NE    ISR        
  1087.     mov    al, 01fh
  1088.     wr_NE    IMR
  1089.  
  1090.     mov    al, 61h
  1091.     wr_NE    CMDR        
  1092.     mov    al, START_PG + 1
  1093.     wr_NE    CURR
  1094.     mov    al, 21h
  1095.     wr_NE    CMDR        
  1096.  
  1097.  
  1098.  
  1099.     mov    DX, CS:io_addr
  1100.     add    DX, RACK        ; DX has address NE/2 Port window 
  1101.  
  1102.     mov     bx, -1                ;setup for Nic's memory test
  1103.  
  1104.     mov    bp, CS:io_addr
  1105.       call    RAMTest              ;16 bit memory test
  1106.  
  1107.         jz    memory_OK            ;if error, report it
  1108.     jmp    no_memory
  1109.  
  1110. memory_OK:
  1111.     ; set up rom_address from addr ROM.
  1112.     mov    al, 12            ;read 6 bytes from PROM (word mode=12)
  1113.     wr_NE    RBCR0
  1114.     mov    al, 0
  1115.     wr_NE    RBCR1
  1116.     wr_NE    RSAR0
  1117.     wr_NE    RSAR1
  1118.     mov    al, MSK_DMA_RD
  1119.     wr_NE    CMDR
  1120.  
  1121.     mov    DX, CS:io_addr
  1122.     add    DX, RACK        ; DX has address NE/2 Port window (?)
  1123.     mov    di, OFFSET rom_address
  1124.     mov    ax, cs
  1125.     mov    es, ax
  1126.     mov    cx, 6
  1127. GetEnetAddress:
  1128.     in    al, dx
  1129.     stosb
  1130.     loop    GetEnetAddress
  1131.  
  1132. ModeCheckDone:
  1133.     mov    ax, cs
  1134.     mov    ds, ax
  1135.     mov    si, OFFSET rom_address
  1136.     mov    cx, 6
  1137.     call    set_address
  1138.  
  1139.     mov     al, 21h
  1140.     wr_NE     CMDR
  1141.  
  1142. SetTXPage:
  1143.     mov     al,0C0h                        ;C000h is used as TX
  1144.         wr_NE    TPSR                ;page because of
  1145.                             ;spurious writes to 
  1146.                         ;Ram during transmits
  1147.                         ;when collisions occur
  1148.     mov    al, 22h
  1149.     jmp    $+2
  1150.     wr_NE    CMDR    
  1151.  
  1152.     mov    al, 0
  1153.     wr_NE    TCR
  1154.  
  1155.     mov    al,0                ;deselect all I/O channels
  1156.     out    SlotSelectRegister,al
  1157.  
  1158.     sti
  1159.     clc
  1160.     ret
  1161.  
  1162. ;**********************************************************************
  1163. ;
  1164. ;    RAM Test
  1165. ;
  1166. ;    assumes:
  1167. ;        BX    has the test value
  1168. ;        BP    points to IOBase
  1169. ;        NIC (8390) has been set to ignore activity on wire
  1170. ;          (loopback mode) and is set to page 0
  1171. ;
  1172. ;    returns:
  1173. ;        Z flag set if no error
  1174. ;        BP    preserved.
  1175. ;        NIC set to page 0
  1176. ;
  1177. ;**********************************************************************
  1178.  
  1179. RAMTest    proc    near
  1180.  
  1181.     lea    dx, [bp].RemoteStartAddress0    ;point at start of RAM
  1182.     xor    al, al
  1183.     out    dx, al
  1184.         inc     dx
  1185.     mov    al, 40h
  1186.     out    dx, al
  1187.  
  1188.     inc    dx                ;set byte count=16k
  1189.     xor    al, al                ; (both 8k RAM chips)
  1190.     out    dx, al
  1191.     inc    dx
  1192.     mov    al, 40h
  1193.     out    dx, al
  1194.  
  1195.     lea    dx, [bp].Command        ;write in all RAM locations
  1196.     mov    al, MSK_DMA_WR            ; what we send
  1197.     out    dx, al
  1198.  
  1199.     lea    dx, [bp].DataPort        ;point at Nic's data port
  1200.  
  1201.     mov    cx, 2000h            ;set count to 8k words
  1202.  
  1203.     mov    ax, bx                ;read in test value
  1204.  
  1205. WriteLoop:
  1206.     inc    ax                ;adjust test pattern
  1207.     out    dx, ax                ;send it out to Nic
  1208.     loop    WriteLoop
  1209.  
  1210.                         ;now read back and compare
  1211.                         ; test pattern from NIC
  1212.  
  1213.     lea    dx, [bp].RemoteStartAddress0    ;point at start of RAM
  1214.     xor    al, al
  1215.     out    dx, al
  1216.         inc     dx
  1217.     mov    al, 40h
  1218.     out    dx, al
  1219.  
  1220.     inc    dx                ;set byte count = 16k
  1221.     xor    al, al
  1222.     out    dx, al
  1223.     inc    dx
  1224.     mov    al, 40h
  1225.     out    dx, al
  1226.  
  1227.     lea    dx, [bp].Command        ;tell NIC to send back test
  1228.     mov    al, MSK_DMA_RD            ; pattern
  1229.     out    dx, al
  1230.  
  1231.     lea    dx, [bp].DataPort        ;point at Nic's data port
  1232.  
  1233.     mov    cx, 2000h            ;setup loop
  1234.  
  1235. ReadLoop:
  1236.     in    ax, dx                ;read in test pattern and
  1237.     inc    bx                ; compare with original
  1238.     cmp    bx, ax
  1239.  
  1240.     loopz    ReadLoop
  1241.  
  1242.     ret                    ;zero flag set if memory error
  1243.  
  1244. RAMTest    endp
  1245.  
  1246.     public    print_parameters
  1247. print_parameters:
  1248. ;echo our command-line parameters
  1249.     ret
  1250.  
  1251.  
  1252. code    ends
  1253.  
  1254.     end
  1255.