home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / lan / driver6s / wd8003e.asm < prev    next >
Assembly Source File  |  1990-04-10  |  34KB  |  1,044 lines

  1. version    equ    5
  2.  
  3.     include    defs.asm
  4.  
  5. ;   PC/FTP Packet Driver source, conforming to version 1.05 of the spec
  6. ;   Updated to version 1.08 Feb. 17, 1989 by Russell Nelson.
  7. ;   Robert C Clements, K1BC,  August 19, 1988
  8. ;   Portions (C) Copyright 1988 Robert C Clements
  9.  
  10. ;   Version 3 updated by Jan Engvald LDC to handle WD8003ET/A (micro channel
  11. ;   card) and to utilize all 32 kbyte memory on the WD8003EBT card.
  12.  
  13. ;   This program is free software; you can redistribute it and/or modify
  14. ;   it under the terms of the GNU General Public License as published by
  15. ;   the Free Software Foundation, version 1.
  16. ;
  17. ;   This program is distributed in the hope that it will be useful,
  18. ;   but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. ;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20. ;   GNU General Public License for more details.
  21. ;
  22. ;   You should have received a copy of the GNU General Public License
  23. ;   along with this program; if not, write to the Free Software
  24. ;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  25.  
  26. code    segment    byte public
  27.     assume    cs:code, ds:code
  28.  
  29. ; Stuff specific to the Western Digital WD003E Ethernet controller board
  30. ; C version by Bob Clements, K1BC, May 1988 for the KA9Q TCP/IP package
  31. ; Symbol prefix "EN" is for Ethernet, Western-digital card
  32.  
  33. ; The EN registers - First, the board registers */
  34.  
  35. EN_CMD        equ    000h    ; Board's command register
  36. EN_REG5        equ    005h    ; New command register (REGISTER 5)
  37. EN_SAPROM    equ    008h    ; Window on station addr prom
  38.  
  39. ; The EN registers - Next, the DS8390 chip registers */
  40. ; There are two (really 3) pages of registers in the chip. You select
  41. ; which page you want, then address them at offsets 10-1F from base.
  42. ; The chip command register (EN_CCMD) appears in both pages.
  43.  
  44. EN_CCMD        equ    010h    ; Chip's command register
  45.  
  46. ; Page 0
  47.  
  48. EN0_STARTPG    equ    011h    ; Starting page of ring bfr
  49. EN0_STOPPG    equ    012h    ; Ending page +1 of ring bfr
  50. EN0_BOUNDARY    equ    013h    ; Boundary page of ring bfr
  51. EN0_TSR        equ    014h    ; Transmit status reg
  52. EN0_TPSR    equ    014h    ; Transmit starting page
  53. EN0_TCNTLO    equ    015h    ; Low  byte of tx byte count
  54. EN0_TCNTHI    equ    016h    ; High byte of tx byte count
  55. EN0_ISR        equ    017h    ; Interrupt status reg
  56. EN0_RCNTLO    equ    01ah    ; Remote byte count reg
  57. EN0_RCNTHI    equ    01bh    ; Remote byte count reg
  58. EN0_RXCR    equ    01ch    ; RX control reg
  59. EN0_TXCR    equ    01dh    ; TX control reg
  60. EN0_COUNTER0    equ    01dh    ; Rcv alignment error counter
  61. EN0_DCFG    equ    01eh    ; Data configuration reg
  62. EN0_COUNTER1    equ    01eh    ; Rcv CRC error counter
  63. EN0_IMR        equ    01fh    ; Interrupt mask reg
  64. EN0_COUNTER2    equ    01fh    ; Rcv missed frame error counter
  65.  
  66. ; Page 1
  67.  
  68. EN1_PHYS    equ    011h    ; This board's physical enet addr
  69. EN1_CURPAG    equ    017h    ; Current memory page
  70. EN1_MULT    equ    018h    ; Desired multicast addr
  71.  
  72.  
  73. ; Board commands in EN_CMD
  74. EN_RESET    equ    080h    ; Reset the board
  75. EN_MEMEN    equ    040h    ; Enable the shared memory
  76. EN_MEM_MASK    equ    03fh    ; B18-B13 of address of the shared memory
  77.  
  78. ; Commands for REG5 register
  79. ENR5_EIL    equ    004h    ; enable 8390 interrupts to bus
  80.  
  81. ; Chip commands in EN_CCMD
  82. ENC_STOP    equ    001h    ; Stop the chip
  83. ENC_START    equ    002h    ; Start the chip
  84. ENC_TRANS    equ    004h    ; Transmit a frame
  85. ENC_NODMA    equ    020h    ; No remote DMA used on this card
  86. ENC_PAGE0    equ    000h    ; Select page 0 of chip registers
  87. ENC_PAGE1    equ    040h    ; Select page 1 of chip registers
  88.  
  89. ; Commands for RX control reg
  90. ENRXCR_MON    equ    020h    ; Monitor mode
  91. ENRXCR_RUNT    equ    002h    ; accept runt packets
  92. ENRXCR_BCST    equ    004h    ; Accept broadcasts
  93. ENRXCR_MULTI    equ    008h    ; Accept multicasts
  94. ENRXCR_PROMP    equ    010h    ; physical promiscuous mode
  95.  
  96. ; Commands for TX control reg
  97. ENTXCR_LOOP    equ    002h    ; Set loopback mode
  98.  
  99. ; Bits in EN0_DCFG - Data config register
  100. ENDCFG_BM8    equ    048h    ; Set burst mode, 8 deep FIFO
  101. ENDCFG_WTS    equ    1    ; Word Transfer Select
  102.  
  103. ; Bits in EN0_ISR - Interrupt status register
  104. ENISR_RX    equ    001h    ; Receiver, no error
  105. ENISR_TX    equ    002h    ; Transmitter, no error
  106. ENISR_RX_ERR    equ    004h    ; Receiver, with error
  107. ENISR_TX_ERR    equ    008h    ; Transmitter, with error
  108. ENISR_OVER    equ    010h    ; Receiver overwrote the ring
  109. ENISR_COUNTERS    equ    020h    ; Counters need emptying
  110. ENISR_RESET    equ    080h    ; Reset completed
  111. ENISR_ALL    equ    01fh    ; Interrupts we will enable
  112.  
  113. ; Bits in received packet status byte and EN0_RSR
  114. ENPS_RXOK    equ    001h    ; Received a good packet
  115.  
  116. ; Bits in TX status reg
  117.  
  118. ENTSR_COLL    equ    004h    ; Collided at least once
  119. ENTSR_COLL16    equ    008h    ; Collided 16 times and was dropped
  120. ENTSR_FU    equ    020h    ; TX FIFO Underrun
  121.  
  122. ; Shared memory management parameters
  123.  
  124. XMIT_MTU    equ    600h    ; Largest packet we have room for.
  125. SM_TSTART_PG    equ    0    ; First page of TX buffer
  126. SM_RSTART_PG    equ    6    ; Starting page of ring
  127. SM_BASE        equ    0C400h    ; Default para where shared memory starts
  128.                 ; Real value set at attach time.
  129. sm_rstop_pg    db    32    ; Last page +1 of ring
  130.  
  131.  
  132. ; Description of header of each packet in receive area of shared memory
  133.  
  134. EN_RBUF_STAT    equ    0    ; Received frame status
  135. EN_RBUF_NXT_PG    equ    1    ; Page after this frame
  136. EN_RBUF_SIZE    equ    2    ; Length of this frame (word access)
  137. EN_RBUF_SIZE_LO    equ    2    ; Length of this frame
  138. EN_RBUF_SIZE_HI    equ    3    ; Length of this frame
  139. EN_RBUF_NHDR    equ    4    ; Length of above header area
  140.  
  141. ; End of WD8003E parameter definitions
  142.  
  143. ; The following three values may be overridden from the command line.
  144. ; If they are omitted from the command line, these defaults are used.
  145.  
  146.     public    int_no, io_addr, mem_base
  147. int_no        db    3,0,0,0        ; Interrupt level
  148. io_addr        dw    0280h,0        ; I/O address for card (jumpers)
  149. mem_base    dw    0d000h,0    ; Shared memory addr (software)
  150.  
  151.     public    driver_class, driver_type, driver_name, driver_function, parameter_list
  152. driver_class    db    1        ;from the packet spec
  153. driver_type    db    14        ;from the packet spec
  154. driver_name    db    'WD8003E',0    ;name of the driver.
  155. driver_function    db    2
  156. parameter_list    label    byte
  157.     db    1    ;major rev of packet driver
  158.     db    9    ;minor rev of packet driver
  159.     db    14    ;length of parameter list
  160.     db    EADDR_LEN    ;length of MAC-layer address
  161.     dw    GIANT    ;MTU, including MAC headers
  162.     dw    MAX_MULTICAST * EADDR_LEN    ;buffer size of multicast addrs
  163.     dw    0    ;(# of back-to-back MTU rcvs) - 1
  164.     dw    0    ;(# of successive xmits) - 1
  165.     dw    0    ;Interrupt # to hook for post-EOI
  166.             ;processing, 0 == none,
  167.  
  168. mcast_list_bits db      0,0,0,0,0,0,0,0 ;Bit mask from last set_multicast_list
  169. mcast_all_flag  db      0               ;Non-zero if hware should have all
  170.                     ; ones in mask rather than this list.
  171.  
  172.     public    rcv_modes
  173. rcv_modes    dw    7        ;number of receive modes in our table.
  174.         dw    0        ;there is no mode 1.
  175.         dw    rcv_mode_1
  176.         dw    rcv_mode_2
  177.         dw    rcv_mode_3
  178.         dw    rcv_mode_4
  179.         dw    rcv_mode_5
  180.         dw    rcv_mode_6
  181. rxcr_bits       db      ENRXCR_BCST     ; Default to ours plus multicast
  182.  
  183.     extrn    sys_features: byte
  184. microchannel    equ    2        ; flag in above byte
  185. ; send_pkt: - The Transmit Frame routine
  186.  
  187.     public    send_pkt
  188. send_pkt:
  189. ;enter with es:di->upcall routine, (0:0) if no upcall is desired.
  190. ;  (only if the high-performance bit is set in driver_function)
  191. ;enter with ds:si -> packet, cx = packet length.
  192. ;exit with nc if ok, or else cy if error, dh set to error number.
  193.     assume    ds:nothing
  194.     loadport        ; Point at chip command register
  195.     setport EN_CCMD        ; ..
  196.     mov bx,    8000h        ; Avoid infinite loop
  197. tx_wait:
  198.     in al,    dx        ; Get chip command state
  199.     test al,ENC_TRANS    ; Is transmitter still running?
  200.     jz    tx_idle        ; Go if free
  201.     dec    bx        ; Count the timeout
  202.     jnz    tx_wait        ; Fall thru if TX is stuck
  203.                 ; Should count these error timeouts
  204.                 ; Maybe need to add recovery logic here
  205. tx_idle:
  206.     cmp    cx,XMIT_MTU    ; Is this packet too large?
  207.     ja    send_pkt_toobig
  208.  
  209.     cmp cx,    RUNT        ; Is the frame long enough?
  210.     jnb    tx_oklen    ; Go if OK
  211.     mov cx,    RUNT        ; Stretch frame to minimum allowed
  212. tx_oklen:
  213.     push    cx        ; Hold count for later
  214.                 ; Now compute destination of move in es:di
  215.     mov ax,    mem_base    ; Compute base of transmit buffer
  216. ;    add ax,    SM_TSTART_PG*16    ; The right page in mem (currently zero)
  217.     mov es,    ax        ; Paragraph of the TX buffer
  218.     xor di,    di        ; Fill starting at beginning of paragraph
  219. ; Can't use movemem which word aligns to the source, but needs to word
  220. ; align to the destination writing to WD8003ET/A. Fortunately works for
  221. ; all cards.
  222.     inc    cx        ; if odd bytes pad to word
  223.     shr    cx,1        ; convert bytes to words
  224.     rep    movsw        ; word access required by MC card
  225.     pop    cx        ; Get back count to give to board
  226.     loadport        ; Base of I/O regs
  227.     setport    EN0_TCNTLO    ; Low byte of TX count
  228.     mov al,    cl        ; Get the count
  229.     out dx,    al        ; Tell card the count
  230.     setport    EN0_TCNTHI    ; High byte of TX count
  231.     mov al,    ch        ; Get the count
  232.     out dx,    al        ; Tell card the count
  233.     setport    EN0_TPSR    ; Transmit Page Start Register
  234.     mov al,    SM_TSTART_PG
  235.     out dx,    al        ; Start the transmitter
  236.     setport    EN_CCMD        ; Chip command reg
  237.     mov al,    ENC_TRANS+ENC_NODMA
  238.     out dx,    al        ; Start the transmitter
  239.     clc
  240.     ret            ; End of transmit-start routine
  241.  
  242. send_pkt_toobig:
  243.     mov    dh,NO_SPACE
  244.     stc
  245.     ret
  246.  
  247.  
  248.     public    get_address
  249. get_address:
  250. ;get the address of the interface.
  251. ;enter with es:di -> place to get the address, cx = size of address buffer.
  252. ;exit with nc, cx = actual size of address, or cy if buffer not big enough.
  253.     assume ds:code
  254.     cmp cx,    EADDR_LEN    ; Caller wants a reasonable length?
  255.     jb    get_addr_x    ; No, fail.
  256.     mov cx,    EADDR_LEN    ; Yes. Set count for loop
  257.     loadport        ; Base of device
  258.     setport    EN_SAPROM    ; Where the address prom is
  259.     cld            ; Make sure string mode is right
  260. get_addr_loop:
  261.     in al,    dx        ; Get a byte of address
  262.     stosb            ; Feed it to caller
  263.     inc    dx        ; Next byte at next I/O port
  264.     loop    get_addr_loop    ; Loop over six bytes
  265.     mov cx,    EADDR_LEN    ; Tell caller how many bytes we fed him
  266.     clc            ; Carry off says success
  267.     ret
  268. get_addr_x:
  269.     stc            ; Tell caller our addr is too big for him
  270.     ret
  271.  
  272.  
  273.     public    set_address
  274. set_address:
  275.     assume    ds:nothing
  276. ;enter with ds:si -> Ethernet address, CX = length of address.
  277. ;exit with nc if okay, or cy, dh=error if any errors.
  278. ;
  279.     cmp    cx,EADDR_LEN        ;ensure that their address is okay.
  280.     je    set_address_4
  281.     mov    dh,BAD_ADDRESS
  282.     stc
  283.     jmp    short set_address_done
  284. set_address_4:
  285.  
  286.     loadport
  287.     setport    EN1_PHYS
  288. set_address_1:
  289.     lodsb
  290.     out    dx,al
  291.     inc    dx
  292.     loop    set_address_1
  293.  
  294. set_address_okay:
  295.     mov    cx,EADDR_LEN        ;return their address length.
  296.     clc
  297. set_address_done:
  298.     push    cs
  299.     pop    ds
  300.     assume    ds:code
  301.     ret
  302.  
  303.  
  304. ; Routines to set address filtering modes in the DS8390
  305. rcv_mode_1:     ; Turn off receiver
  306.     mov al,    ENRXCR_MON      ; Set to monitor for counts but accept none
  307.     jmp short rcv_mode_set
  308. rcv_mode_2:     ; Receive only packets to this interface
  309.     mov al, 0               ; Set for only our packets
  310.     jmp short rcv_mode_set
  311. rcv_mode_3:     ; Mode 2 plus broadcast packets (This is the default)
  312.     mov al,    ENRXCR_BCST     ; Set four ours plus broadcasts
  313.     jmp short rcv_mode_set
  314. rcv_mode_4:     ; Mode 3 plus selected multicast packets
  315.     mov al,    ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
  316.     mov     mcast_all_flag,0
  317.     jmp short rcv_mode_set
  318. rcv_mode_5:     ; Mode 3 plus ALL multicast packets
  319.     mov al,    ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
  320.     mov     mcast_all_flag,1
  321.     jmp short rcv_mode_set
  322. rcv_mode_6:     ; Receive all packets (Promiscuous physical plus all multi)
  323.     mov al,    ENRXCR_BCST+ENRXCR_MULTI+ENRXCR_PROMP
  324.     mov     mcast_all_flag,1
  325. rcv_mode_set:
  326.     push    ax              ; Hold mode until masks are right
  327.     call    set_8390_multi  ; Set the multicast mask bits in chip
  328.     pop     ax
  329.     loadport
  330.     setport    EN0_RXCR    ; Set receiver to selected mode
  331.     out dx,    al
  332.     mov     rxcr_bits,al    ; Save a copy of what we set it to
  333.     ret
  334.  
  335.  
  336.     public    set_multicast_list
  337. set_multicast_list:
  338. ;enter with ds:si ->list of multicast addresses, cx = number of addresses.
  339. ;return nc if we set all of them, or cy,dh=error if we didn't.
  340.     mov    dh,NO_MULTICAST
  341.     stc
  342.     ret
  343.  
  344.  
  345. ; Set the multicast filter mask bits in case promiscuous rcv wanted
  346. set_8390_multi:
  347.     loadport
  348.     setport    EN_CCMD        ; Chip command register
  349.     mov    cx,8        ; Eight bytes of multicast filter
  350.     mov    si,offset mcast_list_bits  ; Where bits are, if not all ones
  351.     push    cs
  352.     pop     ds
  353.     cli            ; Protect from irq changing page bits
  354.     mov    al,ENC_NODMA+ENC_PAGE1
  355.     out    dx,al        ; Switch to page one for writing eaddr
  356.     setport    EN1_MULT    ; Where it goes in 8390
  357.     mov    al,mcast_all_flag  ; Want all ones or just selected bits?
  358.     or    al,al
  359.     jz    set_mcast_2     ; z = just selected ones
  360.     mov    al,0ffh        ; Ones for filter
  361. set_mcast_all:
  362.     out    dx,al        ; Write a mask byte
  363.     inc    dl        ; Step to next one
  364.     jmp    $+2        ; limit chip access rate
  365.     loop    set_mcast_all
  366.     jmp short set_mcast_x
  367.  
  368. set_mcast_2:
  369.     lodsb                   ; Get a byte of mask bits
  370.     out    dx,al        ; Write a mask byte
  371.     inc    dl        ; Step to next I/O register
  372.     jmp    $+2        ; limit chip access rate
  373.     loop    set_mcast_2
  374. set_mcast_x:
  375.     loadport
  376.     setport    EN_CCMD        ; Chip command register
  377.     mov    al,ENC_NODMA+ENC_PAGE0
  378.     out    dx,al        ; Restore to page zero
  379.     sti            ; OK for interrupts now
  380.     ret
  381.  
  382.  
  383.     public    terminate
  384. terminate:
  385.     ret
  386.  
  387.     public    reset_interface
  388. reset_interface:
  389.     assume ds:code
  390.     loadport        ; Base of I/O regs
  391.     setport    EN_CCMD        ; Chip command reg
  392.     mov al,    ENC_STOP+ENC_NODMA
  393.     out dx,    al        ; Stop the DS8390
  394.     setport    EN0_ISR        ; Interrupt status reg
  395.     mov al,    0ffh        ; Clear all pending interrupts
  396.     out dx,    al        ; ..
  397.     setport    EN0_IMR        ; Interrupt mask reg
  398.     xor al,    al        ; Turn off all enables
  399.     setport    EN_REG5
  400.     mov    al,ENR5_EIL
  401.     test    sys_features,microchannel
  402.     jz    reset_no_mc
  403.     out    dx,al            ; enable 8390 interrupts to bus
  404. reset_no_mc:
  405.     ret
  406.  
  407.  
  408. ;called when we want to determine what to do with a received packet.
  409. ;enter with cx = packet length, es:di -> packet type.
  410. ;It returns with es:di = 0 if don't want this type or if no buffer available.
  411.     extrn    recv_find: near
  412.  
  413. ;called after we have copied the packet into the buffer.
  414. ;enter with ds:si ->the packet, cx = length of the packet.
  415.     extrn    recv_copy: near
  416.  
  417.     extrn    count_in_err: near
  418.     extrn    count_out_err: near
  419.  
  420.     public    recv
  421. recv:
  422. ;called from the recv isr.  All registers have been saved, and ds=cs.
  423. ;Actually, not just receive, but all interrupts come here.
  424. ;Upon exit, the interrupt will be acknowledged.
  425.  
  426.     assume    ds:code
  427. check_isr:            ; Was there an interrupt from this card?
  428.     loadport        ; Point at interrupt status register
  429.     setport    EN0_ISR        ; ..
  430.     in al,    dx        ; Get pending interrupts
  431.     and al,    ENISR_ALL    ; Any?
  432.     jnz    isr_test_overrun
  433.     ret            ; Go if none
  434.  
  435. ; First, a messy procedure for handling the case where the rcvr
  436. ; over-runs its ring buffer.  This is spec'ed by National for the chip.
  437. isr_test_overrun: 
  438.     test al,ENISR_OVER    ; Was there an overrun?
  439.     jnz    recv_overrun    ; Go if so.
  440.     jmp    recv_no_overrun    ; Go if not.
  441. recv_overrun:
  442.     setport    EN_CCMD        ; Stop the card
  443.     mov al,    ENC_STOP+ENC_NODMA
  444.     out dx,    al        ; Write "stop" to command register
  445.  
  446.     mov al, ENC_NODMA+ENC_PAGE1    ; Could be in previous out, but
  447.     out dx,al        ; was only tested this way
  448.     setport EN1_CURPAG    ; Get current page
  449.     in al,dx
  450.     mov bl,al        ; save it
  451.     setport    EN_CCMD        ;
  452.     mov al, ENC_NODMA+ENC_PAGE0
  453.     out dx,al        ; Back to page 0
  454.  
  455. ; Remove one frame from the ring
  456.     setport    EN0_BOUNDARY    ; Find end of this frame
  457.     in al,    dx        ; Get memory page number
  458.     inc    al        ; Page plus 1
  459.     cmp al,    sm_rstop_pg    ; Wrapped around ring?
  460.     jnz    rcv_ovr_nwrap    ; Go if not
  461.     mov al,    SM_RSTART_PG    ; Yes, wrap the page pointer
  462. rcv_ovr_nwrap:
  463.  
  464.     cmp    al,bl        ; Check if buffer emptry
  465.     je    rcv_ovr_empty    ; Yes ? Don't receive anything
  466.  
  467.     xor ah,    ah        ; Convert page to segment
  468.     mov cl,    4
  469.     mov bl,    al        ; Page number as arg to rcv_frm
  470.     shl ax,    cl        ; ..
  471.     add ax,    mem_base    ; Page in this memory
  472.     mov es,    ax        ; Segment pointer to the frame header
  473.     push    es        ; Hold this frame pointer for later
  474.     mov ax,    es:[EN_RBUF_STAT]    ; Get the buffer status byte
  475.     test al,ENPS_RXOK    ; Is this frame any good?
  476.     jz    rcv_ovr_ng    ; Skip if not
  477.      call    rcv_frm        ; Yes, go accept it
  478. rcv_ovr_ng:
  479.     pop    es        ; Back to start of this frame
  480.     mov ax,    es:[EN_RBUF_NXT_PG and 0fffeh] ; Get pointer to next frame
  481.     mov    al,ah
  482.     dec    al        ; Back up one page
  483.     cmp al,    SM_RSTART_PG    ; Did it wrap?
  484.     jae    rcv_ovr_nwr2
  485.     mov al,    sm_rstop_pg    ; Yes, back to end of ring
  486.     dec    al
  487. rcv_ovr_nwr2:
  488.     loadport        ; Point at boundary reg
  489.     setport    EN0_BOUNDARY    ; ..
  490.     out dx,    al        ; Set the boundary
  491.  
  492. rcv_ovr_empty:
  493.  
  494.     setport    EN0_RCNTLO    ; Point at byte count regs
  495.     xor al,    al        ; Clear them
  496.     out dx,    al        ; ..
  497.     setport    EN0_RCNTHI
  498.     out dx,    al
  499.     setport    EN0_ISR        ; Point at status reg
  500.     mov cx,    8000h        ; Timeout counter
  501. rcv_ovr_rst_loop:
  502.     in al,    dx        ; Is it finished resetting?
  503.     test al,ENISR_RESET    ; ..
  504.     jmp    $+2        ; limit chip access rate
  505.     loopnz    rcv_ovr_rst_loop; Loop til reset, or til timeout
  506.     loadport        ; Point at Transmit control reg
  507.      setport    EN0_TXCR    ; ..
  508.     mov al,    ENTXCR_LOOP    ; Put transmitter in loopback mode
  509.     out dx,    al        ; ..
  510.     setport    EN_CCMD        ; Point at Chip command reg
  511.     mov al,    ENC_START+ENC_NODMA
  512.     out dx,    al        ; Start the chip running again
  513.     setport    EN0_TXCR    ; Back to TX control reg
  514.     xor al,    al        ; Clear the loopback bit
  515.     out dx,    al        ; ..
  516.     setport    EN0_ISR        ; Point at Interrupt status register
  517.     mov al,    ENISR_OVER    ; Clear the overrun interrupt bit
  518.     out dx,    al        ; ..
  519.     call    count_in_err    ; Count the anomaly
  520.      jmp    check_isr    ; Done with the overrun case
  521.  
  522. recv_no_overrun:
  523. ; Handle receive flags, normal and with error (but not overrun).
  524.     test al,ENISR_RX+ENISR_RX_ERR    ; Frame received without overrun?
  525.     jnz    recv_frame    ; Go if so.
  526.     jmp    recv_no_frame    ; Go if not.
  527. recv_frame:
  528.     loadport        ; Point at Chip's Command Reg
  529.      setport    EN_CCMD        ; ..
  530.     mov al,    ENC_NODMA+ENC_PAGE1
  531.     out dx,    al        ; Switch to page 1 registers
  532.     setport    EN1_CURPAG    ;Get current page of rcv ring
  533.     in al,    dx        ; ..
  534.     mov ah,    al        ; Hold current page in AH
  535.      setport    EN_CCMD        ; Back to page zero registers
  536.     mov al,    ENC_NODMA+ENC_PAGE0
  537.     out dx,    al        ; Switch back to page 0 registers
  538.     setport    EN0_BOUNDARY    ;Get boundary page
  539.     in al,    dx        ; ..
  540.     inc    al        ; Step boundary from last used page
  541.     cmp al,    sm_rstop_pg    ; Wrap if needed
  542.     jne    rx_nwrap3    ; Go if not
  543.     mov al,    SM_RSTART_PG    ; Wrap to first RX page
  544. rx_nwrap3:
  545.     cmp al,    ah        ; Read all the frames?
  546.     je    recv_frame_break    ; Finished them all
  547.     mov bl,    al        ; Page number as arg to rcv_frm
  548.     xor ah,    ah        ; Make segment pointer to this frame
  549.     mov cl,    4        ; 16 * pages = paragraphs
  550.     shl ax,    cl        ; ..
  551.     add ax,    mem_base    ; That far into shared memory
  552.     mov es,    ax        ; Segment part of pointer
  553.     push    es        ; Hold on to this pointer for later
  554.     mov ax,    es:[EN_RBUF_STAT]    ; Get the buffer status byte
  555.     test al,ENPS_RXOK    ; Good frame?
  556.     jz    recv_no_rcv
  557.     call    rcv_frm        ; Yes, go accept it
  558. recv_no_rcv:
  559.     pop    es        ; Back to base of frame
  560.     mov ax,    es:[EN_RBUF_NXT_PG and 0fffeh] ; Start of next frame
  561.     mov    al,ah
  562.     dec    al        ; Make previous page for new boundary
  563.     cmp al,    SM_RSTART_PG    ; Wrap around the bottom?
  564.     jae    rcv_nwrap4
  565.     mov al,    sm_rstop_pg    ; Yes
  566.     dec    al
  567. rcv_nwrap4:
  568.     loadport        ; Point at the Boundary Reg again
  569.      setport    EN0_BOUNDARY    ; ..
  570.     out dx,    al        ; Set new boundary
  571.     jmp    recv_frame    ; See if any more frames
  572.  
  573. recv_frame_break:
  574.     loadport        ; Point at Interrupt Status Reg
  575.      setport    EN0_ISR        ; ..
  576.     mov al,    ENISR_RX+ENISR_RX_ERR+ENISR_OVER
  577.     out dx,    al        ; Clear those requests
  578.     jmp    check_isr    ; See if any other interrupts pending
  579.  
  580. recv_no_frame:                ; Handle transmit flags.
  581.     test al,ENISR_TX+ENISR_TX_ERR    ; Frame transmitted?
  582.     jnz    isr_tx        ; Go if so.
  583.     jmp    isr_no_tx    ; Go if not.
  584. isr_tx:
  585.     mov ah,    al        ; Hold interrupt status bits
  586.     loadport        ; Point at Transmit Status Reg
  587.      setport    EN0_TSR        ; ..
  588.     in al,    dx        ; ..
  589.     test ah,ENISR_TX    ; Non-error TX?
  590.     jz    isr_tx_err    ; No, do TX error completion
  591.     test al,ENTSR_COLL16    ; Jammed for 16 transmit tries?
  592.     jz    isr_tx_njam    ; Go if not
  593.     call    count_out_err    ; Yes, count those
  594. isr_tx_njam:
  595.     setport    EN0_ISR        ; Clear the TX complete flag
  596.     mov al,    ENISR_TX    ; ..
  597.     out dx,    al        ; ..    
  598.     jmp    isr_tx_done
  599. isr_tx_err:
  600.     test al,ENTSR_FU    ; FIFO Underrun?
  601.     jz    isr_txerr_nfu
  602.     call    count_out_err    ; Yes, count those
  603. isr_txerr_nfu:
  604.     loadport        ; Clear the TX error completion flag
  605.     setport    EN0_ISR        ; ..
  606.     mov al,    ENISR_TX_ERR    ; ..
  607.     out dx,    al        ; ..    
  608. isr_tx_done:
  609. ; If TX queue and/or TX shared memory ring buffer were being
  610. ; used, logic to step through them would go here.  However,
  611. ; in this version, we just clear the flags for background to notice.
  612.  
  613.      jmp    check_isr    ; See if any other interrupts on
  614.  
  615. isr_no_tx:
  616. ; Now check to see if any counters are getting full
  617.     test al,ENISR_COUNTERS    ; Interrupt to handle counters?
  618.     jnz    isr_stat    ; nz = yes
  619.     jmp    isr_no_stat
  620. isr_stat:
  621. ; We have to read the counters to clear them and to clear the interrupt.
  622. ; The structure of the PC/FTP driver system doesn't give us
  623. ; anything useful to do with the data, though.
  624.     loadport        ; Point at first counter
  625.      setport    EN0_COUNTER0
  626.     in    al,dx        ; Read the count, ignore it
  627.     setport    EN0_COUNTER1
  628.     in    al,dx        ; Read the count, ignore it
  629.     setport    EN0_COUNTER2
  630.     in    al,dx        ; Read the count, ignore it
  631.     setport    EN0_ISR        ; Clear the statistics completion flag
  632.     mov    al,ENISR_COUNTERS
  633.     out    dx,al
  634. isr_no_stat:
  635.      jmp    check_isr    ; Anything else to do?
  636.  
  637.  
  638. ; Do the work of copying out a receive frame.
  639. ; Called with bl/ the page number of the frame header in shared memory/
  640. ; Also, es/ the paragraph number of that page.
  641.  
  642. rcv_frm:
  643. ; Old version checked size, memory space, queue length here. Now done
  644. ; in higher level code.
  645. ; Set cx to length of this frame.
  646.         mov cx, es:[EN_RBUF_SIZE]       ; Extract size of frame
  647.         sub cx, EN_RBUF_NHDR            ; Less the header stuff
  648.         cmp cx, 1514
  649.         jbe rcv_size_ok                 ; is the size sane? 
  650.         cmp ch, cl                      ; is it starlan bug (dup of low byte)
  651.         jz  rcv_starlan_bug
  652.         mov cx, 1514                    ; cap the length
  653.         jmp rcv_size_ok
  654. rcv_starlan_bug:                        ; fix the starlan bug
  655.         mov ch, es:[EN_RBUF_NXT_PG]     ; Page after this frame
  656.         cmp ch, bl
  657.         ja  rcv_frm_no_wrap
  658.         add ch, sm_rstop_pg             ; Wrap if needed
  659.         dec ch
  660. rcv_frm_no_wrap:
  661.         sub ch, bl
  662.         dec ch
  663. rcv_size_ok:
  664. ; Set es:di to point to Ethernet type field.  es is already at base of
  665. ; page where this frame starts.  Set di after the header and two addresses.
  666.     mov di,    EN_RBUF_NHDR+EADDR_LEN+EADDR_LEN
  667.     push    bx            ; Save page number in bl
  668.     push    cx            ; Save frame size
  669.     push    es
  670.     mov ax,    cs            ; Set ds = code
  671.     mov ds,    ax
  672.     assume    ds:code
  673.     call    recv_find        ; See if type and size are wanted
  674.     pop    ds            ; RX page pointer in ds now
  675.     assume    ds:nothing
  676.     pop    cx
  677.     pop    bx
  678.     cld            ; Copies below are forward, please
  679.     mov ax,    es        ; Did recv_find give us a null pointer?
  680.     or ax,    di        ; ..
  681.     je    rcv_no_copy    ; If null, don't copy the data    
  682.  
  683.     push    cx        ; We will want the count and pointer
  684.     push    es        ;  to hand to client after copying,
  685.     push    di        ;  so save them at this point
  686.  
  687. ;; if ( (((size + 255 + EN_RBUF_NHDR) >> 8) + pg) > sm_rstop_pg){
  688.     mov ax,    cx        ; Length of frame
  689.     add ax,    EN_RBUF_NHDR+255 ; Including the overhead bytes, rounded up
  690.     add ah,    bl        ; Compute page with last byte of data in ah
  691.     cmp ah,    sm_rstop_pg    ; Over the top of the ring?
  692.     ja    rcopy_wrap    ; Yes, move in two pieces
  693.     mov si,    EN_RBUF_NHDR    ; One piece, starts here in first page (in ds)
  694.     jmp    rcopy_one_piece    ; Go move it
  695.  
  696. rcopy_wrap:
  697. ;; Copy in two pieces due to buffer wraparound. */
  698. ;; n = ((sm_rstop_pg - pg) << 8) - EN_RBUF_NHDR;    /* To top of mem */
  699.     mov ah,    sm_rstop_pg    ; Compute length of first part
  700.     sub ah,    bl        ;  as all of the pages up to wrap point
  701.     xor al,    al        ; 16-bit count
  702.     sub ax,    EN_RBUF_NHDR    ; Less the four overhead bytes
  703.     sub cx,    ax        ; Move the rest in second part
  704.     push    cx        ; Save count of second part
  705.     mov cx,    ax        ; Count for first move
  706.     mov si,    EN_RBUF_NHDR    ; ds:si points at first byte to move
  707.     shr cx,    1        ; All above are even numbers, do words.
  708.     rep    movsw        ; Move first part of frame
  709.     mov ax,    mem_base    ; Paragraph of base of shared memory
  710.     mov ds,    ax        ; ..
  711.     mov si,    SM_RSTART_PG*256  ; Offset to start of first receive page
  712.     pop    cx        ; Bytes left to move
  713. rcopy_one_piece:
  714.     shr    cx,1        ; convert bytes to words
  715.     rep    movsw        ; word access desired for MC card
  716.     jnc    rcv_even    ; odd byte left over?
  717.     lodsw            ;   yes, word fetch
  718.     stosb            ;   and byte store
  719. rcv_even:
  720.     pop    si        ; Recover pointer to destination
  721.     pop    ds        ; Tell client it's his source
  722.     pop    cx        ; And it's this long
  723.     assume    ds:nothing
  724.     call    recv_copy    ; Give it to him
  725. rcv_no_copy:
  726.     push    cs        ; Put ds back in code space
  727.     pop    ds        ; ..
  728.     assume    ds:code
  729.     ret            ; That's it for rcv_frm
  730.  
  731.  
  732.     public    recv_exiting
  733. recv_exiting:
  734. ;called from the recv isr after interrupts have been acknowledged.
  735. ;Only ds and ax have been saved.
  736.     assume    ds:nothing
  737.     ret
  738.  
  739.  
  740. ;any code after this will not be kept after initialization.
  741. end_resident    label    byte
  742.  
  743.  
  744.     public    usage_msg
  745. usage_msg    db    "usage: WD8003E <packet_int_no> <int_level> <io_addr> <mem_base>",CR,LF,'$'
  746.  
  747.     public    copyright_msg
  748. copyright_msg    db    "Packet driver for Western Digital WD8003 E EBT EB ET/A and E/A, version "
  749.         db    '0'+majver,".",'0'+version,CR,LF
  750.         db    "Portions Copyright 1988, Robert C. Clements, K1BC",CR,LF,'$'
  751.  
  752. no_board_msg:
  753.     db    "WD8003E apparently not present at this IO address.",CR,LF,'$'
  754. occupied_msg:
  755.     db    "Suggested WD8003E memory address already occupied",CR,LF,'$'
  756. int_no_name    db    "Interrupt number ",'$'
  757. io_addr_name    db    "I/O port ",'$'
  758. mem_base_name    db    "Memory address ",'$'
  759.  
  760. occupied_switch    db    0        ;if zero, don't use occupied test.
  761.  
  762.     extrn    set_recv_isr: near
  763.     extrn    skip_blanks: near
  764.  
  765. ;enter with si -> argument string, di -> word to store.
  766. ;if there is no number, don't change the number.
  767.     extrn    get_number: near
  768.  
  769. ;enter with dx -> name of word, di -> dword to print.
  770.     extrn    print_number: near
  771.  
  772.     public    parse_args
  773. parse_args:
  774.     call    skip_blanks
  775.     cmp    al,'-'            ;did they specify a switch?
  776.     jne    not_switch
  777.     cmp    byte ptr [si+1],'o'    ;did they specify '-o'?
  778.     je    got_occupied_switch
  779.     stc                ;no, must be an error.
  780.     ret
  781. got_occupied_switch:
  782.     mov    occupied_switch,1
  783.     add    si,2            ;skip past the switch's characters.
  784.     jmp    parse_args        ;go parse more arguments.
  785. not_switch:
  786.     mov    di,offset int_no
  787.     call    get_number
  788.     mov    di,offset io_addr
  789.     call    get_number
  790.     mov    di,offset mem_base
  791.     call    get_number
  792.     ret
  793.  
  794.     extrn    etopen_diagn: byte
  795. addr_not_avail:
  796.     mov    dx,offset occupied_msg
  797.     mov    etopen_diagn,34
  798.     jmp    short error_wrt
  799. bad_cksum:
  800. no_memory:
  801.     mov    dx,offset no_board_msg
  802.     mov    etopen_diagn,37
  803. error_wrt:
  804.     mov    ah,9
  805.     int    21h
  806.     stc
  807.     ret
  808.  
  809.  
  810.     public    etopen
  811. etopen:                ; Initialize interface
  812.     loadport        ; First, pulse the board reset
  813.     setport    EN_CMD
  814.     mov al,    EN_RESET
  815.     out dx,    al        ; Turn on board reset bit
  816.     xor al,    al
  817.     out dx,    al        ; Turn off board reset bit
  818.     setport    EN_REG5
  819.     mov    al,ENR5_EIL
  820.     test    sys_features,microchannel
  821.     jz    etopen_no_mc
  822.     out    dx,al        ; enable 8390 interrupts to bus
  823. etopen_no_mc:
  824.     setport    EN_CCMD        ; DS8390 chip's command register
  825.     mov al,    ENC_NODMA+ENC_PAGE0    
  826.     out dx,    al        ; Switch to page zero
  827.     setport    EN0_ISR        ; Clear all interrupt flags
  828.     mov al,    0ffh        ; ..
  829.     out dx,    al        ; ..
  830. ; Copy our Ethernet address from PROM into the DS8390
  831. ; (No provision in driver spec for setting a false address.)
  832.     setport    EN_CCMD        ; Chip command register
  833.     mov al,    ENC_NODMA+ENC_PAGE1
  834.     out dx,    al        ; Switch to page one for writing eaddr
  835.     mov cl,    EADDR_LEN    ; Loop for six bytes
  836.     xor ch,    ch        ; Clear the index of bytes
  837.     xor bx,    bx        ; Clear the addr ROM checksum
  838. cpy_adr_loop:
  839.     loadport        ; Base of registers
  840.     setport    EN_SAPROM    ; Prom address
  841.     add dl,    ch        ; Plus which byte this is
  842.     in al,    dx        ; Get a byte of address
  843.     add    bl,al        ; Compute the checksum
  844.     add dl,    EN1_PHYS-EN_SAPROM ; Point at reg in chip
  845.     out dx,    al        ; Copy that byte
  846.     inc    ch        ; Step the index
  847.     dec    cl        ; Count bytes
  848.     jnz    cpy_adr_loop    ; Loop for six
  849.     loadport        ; Get last two bytes into cksum
  850.     setport    EN_SAPROM+EADDR_LEN
  851.     in al,    dx        ; Get seventh byte
  852.     add bl,    al        ; Add it in
  853.     inc    dx        ; Step to eighth byte
  854.     in al,    dx        ; Get last byte
  855.     add bl,    al        ; Final checksum
  856.     cmp bl, 0ffh        ; Correct?
  857.     jnz    bad_cksum    ; No, board is not happy
  858. ; Clear the multicast filter enables, we don't want any of them.
  859.     mov cl,    8        ; Eight bytes of multicast filter
  860.     xor al,    al        ; Zeros for filter
  861.     loadport        ; Base of multicast filter locations
  862.     setport    EN1_MULT    ; ..
  863. clr_mcast_l:
  864.     out dx,    al        ; Clear a byte
  865.     inc    dl        ; Step to next one
  866.     dec    cl        ; Count 8 filter locs
  867.     jnz    clr_mcast_l    ; ..    
  868.     loadport        ; Base of I/O regs
  869.     setport    EN_CCMD        ; Chip command register
  870.     mov al,    ENC_NODMA+ENC_PAGE0
  871.     out dx,    al        ; Back to page zero
  872.     setport    EN0_DCFG    ; Configure the fifo organization
  873.     mov al,    ENDCFG_BM8    ; Fifo threshold = 8 bytes
  874.     test    sys_features,microchannel
  875.     jz    bytemove
  876.     or    al,ENDCFG_WTS    ; word access for MC card
  877. bytemove:
  878.     out dx,    al
  879.     setport    EN0_RCNTLO    ; Clear the byte count registers
  880.     xor al,    al        ; ..
  881.     out dx,    al
  882.     setport    EN0_RCNTHI
  883.     out dx,    al        ; Clear high byte, too
  884.     setport    EN0_RXCR    ; Set receiver to monitor mode
  885.     mov al,    ENRXCR_MON
  886.     out dx,    al
  887.     setport    EN0_TXCR    ; Set transmitter mode to normal
  888.     xor al,    al
  889.     out dx,    al
  890.  
  891. ; Check if the shared memory address range is availabe to us
  892.     mov    bx,mem_base
  893.     cmp    bh,0c0h            ; low limit is C000
  894.     jae    c0_up
  895.     jmp    no_memory
  896. c0_up:
  897.     cmp    bh,0e0h            ; upper limit is E000
  898.     jb    e0_down
  899.     jmp    no_memory
  900. e0_down:
  901.     test    bx,01ffh        ; must be on a 8 k boundary
  902.     jz    eightk
  903.     jmp    no_memory
  904. eightk:
  905.     mov    di,8*1024/16        ; 8 kbyte
  906.     mov    sm_rstop_pg,32
  907.     test    sys_features,microchannel
  908.     jz    just_8k
  909.     mov    cx,16*1024/16        ; 16 kbytes
  910.     mov    sm_rstop_pg,64
  911. just_8k:
  912.     cmp    occupied_switch,0    ; did they insist?
  913.     jne    is_avail        ; yes, don't check.
  914.     call    occupied_chk        ; check if address range is available
  915.     jnc    is_avail
  916.     jmp    addr_not_avail        ; we HAVE to have at least 8/16 kbyte
  917. is_avail:
  918. ; If I knew how to recognize an WD8003 EBT or EB card, the following code
  919. ; could be skipped with just a slight change in the above code /JE.
  920.     test    sys_features,microchannel
  921.     jnz    not_32k
  922.     mov    di,32*1024/16        ; may be there is space for 32 kbyte
  923.     call    occupied_chk
  924.     jc    not_32k            ; no, then don't try it later either
  925.     and    bh,7
  926.     jnz    not_32k            ; must be on a 32k boundary
  927.     mov    sm_rstop_pg,128        ; yes, there is space for a WD8003EBT
  928. not_32k:
  929.  
  930. ; Turn on the shared memory block
  931.     setport    EN_CMD        ; Point at board command register
  932.     mov ax,    mem_base    ; Find where shared memory will be mapped
  933.     mov al,    ah        ; Shift to right location
  934.     sar al,    1        ;  in the map control word
  935.     and al,    EN_MEM_MASK    ; Just these bits
  936.     or al,    EN_MEMEN    ; Command to turn on map
  937.     test    sys_features,microchannel
  938.     jz    AT_card
  939.     mov    al,EN_MEMEN    ; membase handled different for MC card
  940. AT_card:
  941.     out dx,    al        ; Create that memory
  942.  
  943. ; Find how much memory this card has (without destroying other memory)
  944.     mov    si,ax            ; save bord command value
  945.     mov    es,mem_base
  946.     mov    bl,0FFh            ; first try 32 kbyte (WD8003EBT)
  947.     mov    bh,sm_rstop_pg        ;   or what is available
  948.     dec    bh
  949. memloop:
  950.     dec    bx            ; use even address
  951.     cli                ; disable interrupts
  952.     mov    cx,es:[bx]        ; save old memory contents
  953.     mov    word ptr es:[bx],05A5Ah    ; put testpattern
  954.     loadport
  955.     setport    EN_CCMD            ; drain the board bus for any
  956.     in    al,dx            ;   capacitive memory
  957.     cmp    word ptr es:[bx],05A5Ah    ; any real memory there?
  958.     jne    not_our_mem        ;   no
  959.     setport    EN_CMD            ;   yes
  960.     mov    ax,si
  961.     and    al,0FFh xor EN_MEMEN
  962.     out    dx,al            ; turn off our memory
  963.     jmp    short $+2
  964.     or    al,EN_MEMEN
  965.     cmp    word ptr es:[bx],05A5Ah    ; was it OUR memory?
  966.     out    dx,al
  967.     jmp    short $+2
  968.     mov    es:[bx],cx
  969.     sti
  970.     jne    our_mem            ;   yes, it wasn't there any more
  971. not_our_mem:                ;   no, it was still there
  972.     shr    bx,1            ; test if half as much memory
  973.     cmp    bx,1FFFh        ; down to 8 kbyte
  974.     jae    memloop
  975.     jmp    no_memory        ; no memory at address mem_base
  976. our_mem:                ; it IS our memory!
  977.     inc    bh
  978.     mov    sm_rstop_pg,bh        ; # of 256 byte ring bufs + 1
  979.     mov    ch,bh
  980.     xor    cl,cl
  981.     mov    ax,mem_base
  982.     call    memory_test        ; check all of that memory
  983.     je    mem_ok
  984.     jmp    no_memory
  985. mem_ok:
  986.  
  987. ; Set up control of shared memory, buffer ring, etc.
  988.     setport    EN0_STARTPG    ; Set receiver's first buffer page
  989.     mov al,    SM_RSTART_PG
  990.     out dx,    al
  991.     setport    EN0_STOPPG    ;  and receiver's last buffer page + 1
  992.     mov al,    sm_rstop_pg
  993.     out dx,    al
  994.     setport    EN0_BOUNDARY    ; Set initial "last page we have emptied"
  995.     mov al,    SM_RSTART_PG
  996.     out dx,    al
  997.     setport    EN_CCMD        ; Switch to page one registers
  998.     mov al,    ENC_NODMA+ENC_PAGE1
  999.     out dx,    al
  1000.     setport    EN1_CURPAG    ; Set current shared page for RX to work on
  1001.     mov al,    SM_RSTART_PG+1
  1002.     out dx,    al
  1003.     setport    EN_CCMD        ; Switch back to page zero registers
  1004.     mov al,    ENC_NODMA+ENC_PAGE0
  1005.     out dx,    al
  1006.     setport    EN0_IMR        ; Clear all interrupt enable flags
  1007.     xor al,    al
  1008.     out dx,    al
  1009.     setport    EN0_ISR        ; Clear all interrupt assertion flags
  1010.     mov al,    0ffh        ; again for safety before making the
  1011.     out dx,    al        ; interrupt be enabled
  1012.     call    set_recv_isr    ; Put ourselves in interrupt chain
  1013.     loadport
  1014.     setport    EN_CCMD        ; Now start the DS8390
  1015.     mov al,    ENC_START+ENC_NODMA
  1016.     out dx,    al        ; interrupt be enabled
  1017.     setport    EN0_RXCR    ; Tell it to accept broadcasts
  1018.     mov al,    ENRXCR_BCST
  1019.     out dx,    al
  1020.     setport    EN0_IMR        ; Tell card it can cause these interrupts
  1021.     mov al,    ENISR_ALL
  1022.     out dx,    al
  1023.  
  1024.     mov    di,offset int_no
  1025.     mov    dx,offset int_no_name
  1026.     call    print_number
  1027.     mov    di,offset io_addr
  1028.     mov    dx,offset io_addr_name
  1029.     call    print_number
  1030.     mov    di,offset mem_base
  1031.     mov    dx,offset mem_base_name
  1032.     call    print_number
  1033.  
  1034.     mov    dx,offset end_resident
  1035.     clc
  1036.     ret
  1037.  
  1038.     include    memtest.asm
  1039.     include    occupied.asm
  1040.  
  1041. code    ends
  1042.  
  1043.     end
  1044.