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

  1. page 58, 132
  2.  
  3. ;DBGINT3        equ    0
  4. ;DEBUG        equ    1        ; FOR NOW
  5. version        equ    5        ; version of this file
  6. s8005_version    equ    1        ; SEEQ 8005 stuff (not separate (yet))
  7.  
  8. ;version
  9. ; 1     = initially from Mark Dye
  10. ; 2     = Fix spin loop bug (cx=0 ->65536), broke on slower systems (6MHz)
  11. ; 3     = Fix '-n' bug, fix warnings
  12. ; 4     = Fix Compaq DOS bug (we must do STI after set_recv_isr)
  13. ;         Also, compile with DBGINT3 commmented out.
  14. ;         I thought that 0=false, 1=true, but they were IFDEFfed!
  15. ;         Also, Fix section to ignore broadcasts that we initiated
  16. ; 5     = Release version (nothing changed)
  17.  
  18. ;s8005_version
  19. ;  0    = Initially from MD
  20. ;  1    = Fix  8-bit insb bug (broke on 8088 PCs)
  21.  
  22.  
  23. ; Copyright 1991  DAVID Systems, Inc (Sunnyvale, CA) and Marc S Dye
  24. ; (d.b.a. ayuda Company - Camarillo, CA).  Distribution of this code
  25. ; including any derivation thereof, shall be made so as to afford the
  26. ; recipient of such distribution a copy of this source code, including
  27. ; any alterations, at no charge (except for copying, media, and shipping
  28. ; costs).  Any distribution of this source or derivations must contain
  29. ; this copyright notice.  Documentation describing this work or its
  30. ; derivations must acknowledge the copyright holders herein.  The names
  31. ; of the copyright holders may not be used to advertise, signify, or
  32. ; otherwise characterize any derived work, except for the aforementioned
  33. ; copyright acknowledgement.  This work is provided AS IS, with NO
  34. ; WARRANTIES expressed or implied, NOR any representations made as to
  35. ; its suitability or fitness for any purpose whatsoever.  The recipient
  36. ; assumes full responsibility for use of this work.
  37. ; Some patches made by:
  38. ;   Jeff Douglass
  39.  
  40. ifdef    DEBUG
  41. private        macro    SYM
  42.         public    SYM
  43.         endm
  44. else
  45. private        macro    SYM
  46.         endm
  47. endif
  48.  
  49.     include    defs.asm
  50. ; Ethernet-2 header length defined
  51. ;#
  52. ENET_HDR    equ    2*EADDR_LEN + 4  ;was +2
  53.  
  54. ; assemble a select few instructions (16-bit i/o) from the extended set
  55. ; otherwise, must do only 8086-compatible stuff!
  56.         .286p
  57.  
  58.  
  59. ; Definitions of interface to the SEEQ 8005 EDLC.  See the SEEQ data book
  60. ; for complete details of this device.
  61.  
  62. ; 8005 directly-addressable register offsets (from i/o base address)
  63. COMSTAT_R    equ    0        ; COMMAND / STATUS
  64. CONFIG1_R    equ    2        ; CONFIGURATION 1
  65. CONFIG2_R    equ    4        ; CONFIGURATION 2
  66. REA_PTR_R    equ    6        ; Receive End Area Pointer
  67. BUFWIN_R    equ    8        ; Buffer Window
  68. RX_PTR_R    equ    10        ; Receive Pointer
  69. TX_PTR_R    equ    12        ; Transmit Pointer
  70. DMA_ADDR_R    equ    14        ; DMA Address
  71.  
  72. ; 8005 COMMAND register
  73. DMA_INT_EN    equ    0001h
  74. RX_INT_EN    equ    0002h
  75. TX_INT_EN    equ    0004h
  76. WINDOW_INT_EN    equ    0008h
  77. DMA_INT_ACK    equ    0010h
  78. RX_INT_ACK    equ    0020h
  79. TX_INT_ACK    equ    0040h
  80. WINDOW_INT_ACK    equ    0080h
  81. SET_DMA_ON    equ    0100h
  82. SET_RX_ON    equ    0200h
  83. SET_TX_ON    equ    0400h
  84. SET_DMA_OFF    equ    0800h
  85. SET_RX_OFF    equ    1000h
  86. SET_TX_OFF    equ    2000h
  87. FIFO_READ    equ    4000h
  88. FIFO_WRITE    equ    8000h
  89.  
  90. ; 8005 STATUS register
  91. DMA_INT        equ    0010h
  92. RX_INT        equ    0020h
  93. TX_INT        equ    0040h
  94. WINDOW_INT    equ    0080h
  95. DMA_ON        equ    0100h
  96. RX_ON        equ    0200h
  97. TX_ON        equ    0400h
  98. FIFO_FULL    equ    2000h
  99. FIFO_EMPTY    equ    4000h
  100. FIFO_DIR    equ    8000h
  101.  
  102. ; 8005 CONFIGURATION 1 register
  103. MATCH_BITS_M    equ    0C000h
  104. MATCH_ONLY    equ    0000h
  105. MATCH_BROAD    equ    4000h
  106. MATCH_MULTI    equ    8000h
  107. MATCH_ALL    equ    0C000h
  108. ALL_STATIONS    equ    3F00h
  109. STATION_0_EN    equ    0100h
  110. STATION_1_EN    equ    0200h
  111. STATION_2_EN    equ    0400h
  112. STATION_3_EN    equ    0800h
  113. STATION_4_EN    equ    1000h
  114. STATION_5_EN    equ    2000h
  115. DMA_LENGTH_M    equ    00C0h
  116. NBYTES_1    equ    0000h
  117. NBYTES_2    equ    0040h
  118. NBYTES_4    equ    0080h
  119. NBYTES_8    equ    00C0h
  120. DMA_INTERVAL_M    equ    0030h
  121. CONTINUOUS    equ    0000h
  122. DELAY_800    equ    0010h
  123. DELAY_1600    equ    0020h
  124. DELAY_3200    equ    0030h
  125. BUFFER_CODE_M    equ    000Fh
  126. STATION_0_SEL    equ    0000h
  127. STATION_1_SEL    equ    0001h
  128. STATION_2_SEL    equ    0002h
  129. STATION_3_SEL    equ    0003h
  130. STATION_4_SEL    equ    0004h
  131. STATION_5_SEL    equ    0005h
  132. PROM_SEL    equ    0006h
  133. TEA_SEL        equ    0007h
  134. BUFFER_MEM_SEL    equ    0008h
  135. INT_VECTOR_SEL    equ    0009h
  136. ; Ether-T PC/AT device configuration register -- access is overloaded onto
  137. ; the set of registers accessible via the reserved range in CONFIGURATION 1
  138. CARD_CONFIG_SEL    equ    000Fh
  139. SET_16BITMODE_M    equ    0080h        ; OR mask for 16 bit mode
  140.  
  141. ; 8005 CONFIGURATION 2 register
  142. BYTE_SWAP    equ    0001h
  143. AUTO_UPDATE_REA    equ    0002h
  144. CRC_ERR_EN    equ    0008h
  145. DRIBBLE_EN    equ    0010h
  146. SHORT_FRAME_EN    equ    0020h
  147. SLOT_TIME_SEL    equ    0040h
  148. XMIT_NO_PREAM    equ    0080h
  149. ADDR_LENGTH    equ    0100h
  150. RECEIVE_CRC    equ    0200h
  151. XMIT_NO_CRC    equ    0400h
  152. LOOPBACK_EN    equ    0800h
  153. KILL_WATCHDOG    equ    1000h
  154. RESET        equ    8000h
  155.  
  156. ; 8005 TRANSMIT HEADER COMMAND BYTE  (byte 3 of transmit packet header)
  157. BABBLE_INT_EN    equ    01h
  158. COLL_INT_EN    equ    02h
  159. COLL_16_INT_EN    equ    04h
  160. XMIT_OK_INT_EN    equ    08h
  161. DATA_FOLLOWS    equ    20h        ; also in RECEIVE HEADER COMMAND BYTE
  162. CHAIN_CONTINUE    equ    40h        ; also in RECEIVE HEADER COMMAND BYTE
  163. PACKET_PRESENT    equ    80h
  164. TX_HEADER_Z    equ    4        ; # of bytes in a TX header
  165.  
  166. ; 8005 TRANSMIT HEADER STATUS BYTE  (byte 4 of transmit packet header)
  167. BABBLE_ERR    equ    01h
  168. COLL        equ    02h
  169. COLL_16_ERR    equ    04h
  170. HDR_DONE    equ    80h
  171.  
  172. ; 8005 RECEIVE HEADER COMMAND BYTE (byte 3 of receive packet header)
  173. ;DATA_FOLLOWS        20h        from TRANSMIT HEADER COMMAND BYTE
  174. ;CHAIN_CONTINUE        40h        from TRANSMIT HEADER COMMAND BYTE
  175.  
  176. ; 8005 RECEIVE HEADER STATUS BYTE (byte 4 of receive packet header)
  177. OVERSIZE    equ    01h
  178. CRC_ERR        equ    02h
  179. DRIBBLE_ERR    equ    04h
  180. SHORT_FRAME    equ    08h
  181.  
  182. ; 8005 Receive Area Base Pointer
  183. RCV_PTR        equ    ((4 + GIANT + 4 + 255) AND NOT 0FFh)
  184. ; 8005 Transmit End Area Pointer (upper 8-bits)
  185. TEA_INIT    equ    (((RCV_PTR - 1) / 256) AND 0FFh)
  186.  
  187. ; macro to provide a delay after dinking w/ 8005 registers
  188. io_delay    macro    CNTVAR
  189.         local    io_delay_loop, skip_delay
  190.         push    cx
  191.         mov    cx, CNTVAR
  192.                 jcxz    skip_delay
  193. io_delay_loop:    nop
  194.         loop    io_delay_loop
  195. skip_delay:    pop    cx
  196.         endm
  197.  
  198. ; macros to get and set the various device registers
  199. ; these assume that a proper 'loadport' context exists, that input/output
  200. ; register values are in AX (for word i/o) or AL (for byte i/o)
  201. get_r        macro    R
  202.         setport    R
  203.         call    inb_8005
  204.         endm
  205.  
  206. getw_r        macro    R
  207.         setport    R
  208.         call    [inw_fn]
  209.         endm
  210.  
  211. set_r        macro    R
  212.         setport    R
  213.         call    outb_8005
  214.         endm
  215.  
  216. setw_r        macro    R
  217.         setport    R
  218.         call    [outw_fn]
  219.         endm
  220.  
  221. ; wait for the 8005 FIFO to be free
  222. fifo_wait    macro
  223.         local    fifo_wait_loop
  224.         setport    COMSTAT_R    ; fix access within the loop
  225. fifo_wait_loop:    getw_r    COMSTAT_R    ; wait for the FIFO to be free to use
  226.         and    ax, FIFO_EMPTY+FIFO_DIR
  227.         cmp    ax, FIFO_EMPTY
  228.         jne    fifo_wait_loop    ; loop
  229.         endm
  230.  
  231.  
  232. CODE    segment    byte public
  233.     assume    cs:CODE, ds:CODE
  234.  
  235.     public    INT_NO, IO_ADDR
  236. INT_NO        db    3, 0, 0, 0    ; interrupt number
  237. IO_ADDR        dw    300h, 0        ; (factory) i/o port address
  238.  
  239.     public    DRIVER_CLASS, DRIVER_TYPE, DRIVER_NAME, DRIVER_FUNCTION
  240.     public    PARAMETER_LIST, INT_NUM
  241. DRIVER_CLASS    db    BLUEBOOK, IEEE8023, 0
  242. DRIVER_TYPE    db    70        ; Ether-T PC/AT, specifically
  243. DRIVER_NAME    db    "Ether-T PC/AT", 0  ; name of the driver
  244. DRIVER_FUNCTION    db    2        ; standard plus extensions (except
  245.                     ;  specific multicast)
  246. PARAMETER_LIST    label    byte
  247.         db    1        ; major rev of packet driver spec
  248.         db    9        ; minor rev of packet driver spec
  249.         db    14        ; length of parameter list
  250.         db    EADDR_LEN    ; length of MAC-layer address
  251.         dw    GIANT        ; MTU, including headers
  252.         dw    MAX_MULTICAST * EADDR_LEN
  253.                     ; buffer size of multicast addresses
  254.         dw    0        ; (# of back-to-back MTU recvs) - 1
  255.         dw    0        ; (# of successive xmits) - 1
  256. INT_NUM        dw    0        ; interrupt # to hook for post-EOI
  257.                     ;  processing, 0 == none
  258.  
  259.     public    RCV_MODES
  260. ; Receive mode set-up dispatch table.  Refers to actual code frags below that 
  261. ; do the job.
  262. RCV_MODES    dw    7        ; number of receive modes in our table
  263.         dw    0        ; none exists
  264.         dw    rcv_mode_1    ; receiver off
  265.         dw    rcv_mode_2    ; only packets for this station
  266.         dw    rcv_mode_3    ; mode 2 plus broadcast
  267.         dw    0        ; mode 3 plus limited multicast
  268.         dw    rcv_mode_5    ; mode 3 plus all multicast
  269.         dw    rcv_mode_6    ; all packets
  270.  
  271. ; per-device instance structures -- keep state of the device -- ones marked
  272. ; as (copy) are soft copies of things actually on the device
  273.     private    command_r
  274. command_r    dw    0        ; COMMAND (copy, sort of)
  275.     private    config1_r_copy
  276. config1_r_copy    dw    0        ; CONFIGURATION 1 (copy)
  277.     private    config2_r_copy
  278. config2_r_copy    dw    0        ; CONFIGURATION 2 (copy)
  279.     private    cardconfig_r
  280. cardconfig_r    db    0        ; CARD CONFIGURATION (copy)
  281.     private    rcv_errors
  282. rcv_errors    db    0        ; want to receive even flawed packets
  283.     private    rcv_ptr_copy
  284. rcv_ptr_copy        dw    0        ; current (next) received packet ptr
  285.     private    transmit
  286. transmit    db    0        ; currently transmitting
  287.         db    ?
  288. station_0       db      6 dup(0)        ;current address (for disallowing
  289.                                         ; broadcast packets)(JLD)(9-25-91)
  290.  
  291. ; i/o instruction processing variables
  292.     private    delay_mult
  293. delay_mult    dw    10, 0        ; delay multiplier / divisor - is
  294.                     ; a tens fraction: delay becomes
  295.                     ; (calibrated_delay * delay_mult) / 10
  296.     private    io_16
  297. io_16        db    0        ; do 16-bit i/o?
  298.         db    ?
  299.     private    io_delay_cnt
  300. io_delay_cnt    dw    1, 0        ; # of NOP spins to do to effect the
  301.                     ; required delay (2000 ns) for the
  302.                     ; worst-case i/o access
  303.     private    iowm_delay_cnt
  304. iowm_delay_cnt    dw    1, 0        ; # of NOP spins to effect the required
  305.                     ; delay (800 ns) for a write to the
  306.                     ; buffer memory in the window register
  307.     private    inw_fn
  308. inw_fn        dw    OFFSET inwb_8005  ; function to input a word (w/ delay)
  309.     private    outw_fn
  310. outw_fn        dw    OFFSET outwb_8005 ; function to output a word (w/ delay)
  311.  
  312. ; interrupt processing variables
  313.     private    int_test
  314. int_test    db    0        ; in the initial interrupt testing?
  315.     private    int_tested
  316. int_tested    db    0        ; was initial interrupt test successful?
  317.  
  318. ifdef    DEBUG
  319.     public    ghost_int_c, send_wait_c
  320. ghost_int_c    dw    0        ; ghost (empty) interrupt counter
  321. rx_restart    dw    0        ; stalled receiver restart counter
  322. send_wait_c    dw    0        ; previous send completion wait counter
  323. endif
  324.  
  325.     extrn    THEIR_ISR : dword
  326.  
  327.     extrn    COUNT_IN_ERR : near
  328.     extrn    COUNT_OUT_ERR : near
  329.     extrn    GET_NUMBER : near
  330.     extrn    MASKINT : near
  331.     extrn    PRINT_NUMBER : near
  332.     extrn    RECV_COPY : near
  333.     extrn    RECV_FIND : near
  334.     extrn    SET_RECV_ISR : near
  335.     extrn    UNMASKINT : near
  336.  
  337.  
  338.     public bad_command_intercept
  339. bad_command_intercept:
  340. ;called with ah=command, unknown to the skeleton.
  341. ;exit with nc if okay, cy, dh=error if not.
  342.     mov    dh,BAD_COMMAND
  343.     stc
  344.     ret
  345.  
  346.     public    AS_SEND_PKT
  347. ; Asynchronous packet transmit routine (high-performance drivers only).
  348. ; Entry with:
  349. ;    ES:DI    control block pointer
  350. ;    DS:SI    packet to send
  351. ;    CX    packet length
  352. ; Interrupts *may* be enabled.  Returns:
  353. ;    ES:DI    preserved from call (control block pointer)
  354. ;       and
  355. ;    NC    if OK
  356. ;       or
  357. ;    CY    if transmission error
  358. ;    DH    error code
  359. ; Interrupt disablement on entry is preserved on exit.
  360. AS_SEND_PKT:
  361.         assume    ds:nothing
  362.         ret
  363.  
  364.  
  365.     public    DROP_PKT
  366. ; Drop a packet from the queue (high-performance drivers only.)
  367. DROP_PKT:
  368.         assume    ds:nothing
  369.         ret
  370.  
  371.  
  372.     private    inb_8005
  373. ; input a byte from an arbitrary 8005 port
  374. inb_8005:
  375.         assume    ds:nothing
  376.         in    al, dx
  377.         io_delay  io_delay_cnt    ; generic delay
  378.         ret
  379.  
  380.  
  381.     private    inwb_8005
  382. ; input a word from an arbitrary 8005 port -- 8 bit version
  383. inwb_8005:
  384.         assume    ds:nothing
  385.         in    al, dx
  386.         io_delay  io_delay_cnt    ; generic delay
  387.         inc    dx
  388.         xchg    al, ah
  389.         in    al, dx
  390.         xchg    al, ah
  391.         io_delay  io_delay_cnt    ; generic delay
  392.         dec    dx        ; to keep 'setport' in synch
  393.         ret
  394.  
  395.  
  396.     private    inw_8005
  397. ; input a word from an arbitrary 8005 port -- 16 bit version
  398. inw_8005:
  399.         assume    ds:nothing
  400.         in    ax, dx
  401.         io_delay  io_delay_cnt    ; generic delay
  402.         ret
  403.  
  404.  
  405.     private    outb_8005
  406. ; output a byte to an arbitrary 8005 port
  407. outb_8005:
  408.         assume    ds:nothing
  409.         out    dx, al
  410.         io_delay  io_delay_cnt    ; generic delay
  411.         ret
  412.  
  413.  
  414.     private    outwb_8005
  415. ; output a word to an arbitrary 8005 port -- 8 bit version
  416. outwb_8005:
  417.         assume    ds:nothing
  418.         out    dx, al
  419.         io_delay  io_delay_cnt    ; generic delay
  420.         inc    dx
  421.         xchg    al, ah
  422.         out    dx, al
  423.         xchg    al, ah
  424.         io_delay  io_delay_cnt    ; generic delay
  425.         dec    dx        ; to keep 'setport' in synch
  426.         ret
  427.  
  428.  
  429.     private    outw_8005
  430. ; output a word to an arbitrary 8005 port -- 16 bit version
  431. outw_8005:
  432.         assume    ds:nothing
  433.         out    dx, ax
  434.         io_delay  io_delay_cnt    ; generic delay
  435.         ret
  436.  
  437.  
  438.     private    rcv_mode_1
  439.     private    rcv_mode_2
  440.     private    rcv_mode_3
  441.     private    rcv_mode_5
  442.     private    rcv_mode_6
  443. ; Receive mode set-up routines.  Dispatched to out of the RCV_MODES
  444. ; switch (above) or called directly by ETOPEN (once).
  445.         assume    ds:CODE
  446. rcv_mode_1:    ; turn off receiver
  447.         call    turn_rx_off
  448.         ret
  449. rcv_mode_2:    ; only packets to this station address
  450.         mov    ax, MATCH_ONLY    ; config 1
  451.         mov    dx, 0        ; config 2
  452.         jmp    SHORT rcv_mode_common
  453. rcv_mode_3:    ; mode 2 plus broadcast packets
  454.         mov    ax, MATCH_BROAD    ; config 1
  455.         mov    dx, CRC_ERR_EN+DRIBBLE_EN+SHORT_FRAME_EN ; config 2
  456.         jmp    SHORT rcv_mode_common
  457. rcv_mode_5:    ; mode 3 plus all multicast packets
  458.         mov    ax, MATCH_MULTI    ; config 1
  459.         mov    dx, CRC_ERR_EN+DRIBBLE_EN+SHORT_FRAME_EN ; config 2
  460.         jmp    SHORT rcv_mode_common
  461. rcv_mode_6:    ; all packets (promiscuous, including errors)
  462.         mov    ax, MATCH_ALL    ; config 1
  463.         mov    dx, CRC_ERR_EN+DRIBBLE_EN+SHORT_FRAME_EN ; config 2
  464. rcv_mode_common:
  465.         push    dx        ; save the changes while we
  466.         push    ax        ;   wang on the receiver
  467.         call    turn_rx_off        ; shut down whilst we play
  468.         loadport
  469.         pop    bx        ; alter the CONFIGURATION 1 register
  470.         mov    ax, config1_r_copy
  471.         and    ax, NOT MATCH_BITS_M
  472.         or    ax, bx
  473.         mov    rcv_errors, 0
  474.         cmp    bx, MATCH_ALL    ; figure if we want to see errors
  475.         jne    rcv_mode_common_1
  476.         mov    rcv_errors, 1
  477. rcv_mode_common_1:
  478.         mov    config1_r_copy, ax
  479.         setw_r    CONFIG1_R
  480.         pop    bx        ; alter the CONFIGURATION 2 register
  481.         mov    ax, config2_r_copy
  482.         and    ax, NOT (CRC_ERR_EN+DRIBBLE_EN+SHORT_FRAME_EN)
  483.         or    ax, bx
  484.         mov    config2_r_copy, ax
  485.         setw_r    CONFIG2_R
  486.         call    turn_rx_on        ; drop trou now, brown cow
  487.         ret
  488.  
  489.  
  490.     private    turn_rcv_on
  491. ; turn the receiver back on, as per the COMMAND register atop the stack
  492. ; removes the COMMAND register upon return
  493. rcv_on:
  494.         assume    ds:CODE
  495.         mov    bx, sp        ; get former COMMAND register
  496.         mov    ax, ss:[bx+2]
  497.         test    ax, SET_RX_OFF    ; see if it's to go back on
  498.         jnz    rcv_on_1
  499.         mov    command_r, ax    ; restore it
  500.         call    turn_rx_on
  501. rcv_on_1:    ret    2        ; clear stack of COMMAND register too
  502.  
  503.  
  504.     private    rcvxmt_off
  505. ; forcibly turn off the receiver and/or transmitter -- if the transmitter
  506. ; is running, this will abort the transmission
  507. ; leaves the prior COMMAND register on top of the stack (for use by a
  508. ; matching 'rcv_on' call)
  509. rcvxmt_off:
  510.         assume    ds:CODE
  511.         pop    bx        ; return address
  512.         push    command_r    ; left for 'rcv_on'
  513.         or    command_r, SET_TX_OFF
  514.         mov    transmit, 0    ; not doing it now
  515.         call    turn_rx_off
  516.         jmp    bx        ; return
  517.  
  518.  
  519.     public    RECV
  520. ; Part one of the ISR receive packet processing.  Called before interrupt
  521. ; acknowledgement.  All registers have been saved, DS == CS, and we're
  522. ; running on a private stack with interrupts disabled.
  523. RECV:
  524.         assume    ds:CODE
  525. ifdef    DBGINT3
  526.         int    03h        ; Danger, Will Robinson!
  527. endif
  528.         loadport
  529.         ; check for and acknowledge the 8005 receive Interrupt
  530. ifdef    DEBUG
  531.         getw_r    COMSTAT_R
  532.         test    ax, RX_INT
  533.         jnz    recv_1
  534.         inc    ghost_int_c    ; ghost interrupt counter
  535. recv_1:
  536. endif
  537.         mov    ax, command_r    ; acknowledge the Rx Interrupt
  538.         or    ax, RX_INT_ACK
  539.         setw_r    COMSTAT_R
  540.         cmp    int_test, 0    ; is this interrupt a test one?
  541.         je    recv_setup
  542.         inc    int_tested    ; indicated tested and be done
  543.         ret
  544.  
  545. recv_setup:    ; set-up DMA access of the buffer read memory
  546.         mov    ax, config1_r_copy    ; select BUFFER MEMORY in BUFWIN
  547.         or    ax, BUFFER_MEM_SEL
  548.         setw_r    CONFIG1_R
  549.  
  550.         setport    DMA_ADDR_R    ; code block port state synch
  551. ; COME HERE w/ port @ DMA_ADDR_R
  552. recv_rcvptr:    mov    ax, rcv_ptr_copy    ; address the next RECEIVE HEADER
  553.         setw_r    DMA_ADDR_R
  554.         mov    ax, command_r    ; set the FIFO to READ now
  555.         or    ax, FIFO_READ
  556.         setw_r    COMSTAT_R
  557.  
  558.         setport    BUFWIN_R    ; code block port state synch
  559. ; COME HERE w/ port @ BUFWIN_R
  560. recv_loop:    ; check for an end-of-list header (0 next pointer)
  561.         getw_r    BUFWIN_R    ; get the HEADER pointer
  562.         cmp    al, 0        ; check for end of receive list
  563.         je    recv_loop_1    ;  looking only at first byte
  564.         xchg    al, ah        ; swap to proper host order
  565.         mov    cx, ax        ; save pointer away
  566.         getw_r    BUFWIN_R    ; get the HEADER & PACKET STATI
  567.         test    al, CHAIN_CONTINUE  ; test for completion
  568.         xchg    al, ah        ; AL now holds PACKET STATUS byte
  569.         jnz    recv_continue
  570. recv_loop_1:    jmp    recv_done
  571.  
  572. recv_continue:    ; process a new frame - compute length and check it
  573.         push    cx        ; squirrel away HEADER POINTER
  574.         sub    cx, rcv_ptr_copy    ; compute length, checking for wrappage
  575.         jae    recv_continue_1
  576.         sub    cx, RCV_PTR    ; adjust for wrap, sans xmit area
  577. recv_continue_1:
  578.         sub    cx, 4        ; less size of 8005 garf
  579.         cmp    cx, ENET_HDR    ; < a header is tallied and pitched
  580.         jb    recv_continue_2
  581.         cmp    cx, GIANT    ; > GIANT likewise
  582.         ja    recv_continue_2
  583.         test    al, SHORT_FRAME+DRIBBLE_ERR+CRC_ERR
  584.         jz    recv_alloc    ; no error is a-OK
  585.         cmp    rcv_errors, 0    ; see if we have to allow bad ones
  586.         jne    recv_alloc    ; yes, keep this dreck too
  587. recv_continue_2:
  588.         call    COUNT_IN_ERR    ; tally this error
  589.         jmp    recv_sail_it
  590.  
  591. recv_alloc:    ; this one we want - allocate enough space on the stack
  592.         ;  to hold the Ethernet-2 packet header (so we can upcall
  593.         ;  pointing at the type field)
  594.         mov    bx, cx        ; save the real length
  595.         mov    cx, ENET_HDR
  596.         sub    sp, cx        ; allocate a header on the stack
  597.         mov    di, sp        ;   and point at it w/ DI
  598.         mov    ax, ss        ; set-up ES too
  599.         mov    es, ax
  600.         cmp    io_16, 0    ; do we do it fast or slow?
  601.         je    recv_8bit_hdr
  602.         sar    cx, 1        ; turn into words
  603. ;    rep    insw
  604. recv_16bit_hdr:
  605.         insw            ; 16-bit input    
  606.         io_delay iowm_delay_cnt    ; reduced delay w/i loop
  607.         loop    recv_16bit_hdr
  608.         jmp    SHORT recv_upcall1
  609. recv_8bit_hdr:                ; 8-bit input
  610.         in    al, dx
  611.         mov    es:[di], al
  612.         inc    di
  613.         loop    recv_8bit_hdr
  614.  
  615. recv_upcall1:
  616. ;#              ; check to make sure this isn't our own broadcast(JLD)(9-25-91)
  617.                 push    di
  618.             mov    di, sp        ; point at the destination
  619.                 add     di, 2           ; which is 2 bytes away(remember push di?)
  620.                 mov     ax, 0ffffh
  621.                 mov     cx, 6/2
  622.             repe scasw
  623.                 jne     NotBroadcast
  624.             
  625.                 push    si
  626.                 mov     si, offset station_0
  627.                 mov     cx, 6/2
  628.             repe cmps   word ptr cs:[si], word ptr es:[di]
  629.                 pop     si
  630.             
  631.                 jne     NotBroadcast
  632.  
  633.                 pop     di
  634.         add    sp, ENET_HDR    ; pop the Ethernet-2 header from stack
  635.                                         ; I forgot this until it bit me (11-6)
  636.                 jmp     recv_sail_it    ; if it's us, bail out!
  637. NotBroadcast:
  638.                 pop     di
  639.  
  640.  
  641.                 ; prepare an upcall with CX == length, ES:DI pointing at type,
  642.         ;  DL == packet class number (V8)
  643.         mov    di, sp        ; point at the type field
  644.         add    di, EADDR_LEN*2
  645.         mov    cx, bx        ; get the real length for RECV_FIND
  646.         push    bx        ; save the real length
  647.         push    dx        ;  and save our precious port setup too
  648. ;#
  649.     mov    dl, BLUEBOOK        ;assume bluebook Ethernet.
  650.     mov    ax, es:[di]
  651.     xchg    ah, al
  652.     cmp     ax, 1500
  653.     ja    BlueBookPacket
  654.     inc    di            ;set di to 802.2 header
  655.     inc    di
  656.     mov    dl, IEEE8023
  657. BlueBookPacket:
  658. ;;;;        mov    dl, BLUEBOOK    ; Ethernet-2 'Bluebook' (V8)
  659.         call    RECV_FIND
  660.  
  661.         pop    dx        ; get back port setup
  662.         pop    bx        ;  and true packet length
  663.         mov    ax, es        ; see if the client passed on this one
  664.         or    ax, di
  665.         jz    recv_sail_pop
  666.  
  667.         mov    si, sp        ; prepare to MOVS the Ethernet-2 header
  668.         mov    ax, ss
  669.         mov    ds, ax
  670.         assume    ds:nothing
  671.         mov    cx, ENET_HDR
  672.         push    di        ; save client packet base pointer
  673.     rep    movsb
  674.         pop    si        ; restore client packet base pointer
  675.         add    sp, ENET_HDR    ; pop the Ethernet-2 header from stack
  676.  
  677.         mov    cx, bx        ; compute residual length to bring in
  678.         sub    cx, ENET_HDR
  679.         cmp    io_16, 0    ; do we do it fast or slow?
  680.         je    recv_8bit_data
  681.  
  682.         sar    cx, 1        ; /2: compute # of words to transfer
  683. ;    rep    insw            ; loosely termed "full-bandwidth i/o"
  684. recv_16bit_data:
  685.         insw
  686.         io_delay iowm_delay_cnt    ; reduced delay w/i loop
  687.         loop    recv_16bit_data
  688.  
  689.         mov    cx, bx        ; get length back one more time
  690.         test    bl, 1        ; see if there's a dangling byte
  691.         jz    recv_upcall2
  692.         mov    cx, 1        ; set-up to fall through to 8-bit
  693.  
  694. recv_8bit_data:    in    al, dx
  695.         mov    es:[di], al
  696.         inc    di
  697.         loop    recv_8bit_data
  698.         mov    cx, bx        ; get length back one more time
  699.         xor    bl, bl        ; for sail testing after upcall2
  700.  
  701. recv_upcall2:    ; prepare an upcall with CX == length, DS:SI pointing at
  702.         ;  client-allocated and now filled packet
  703.         mov    ax, es
  704.         mov    ds, ax
  705.         push    dx        ; save our precious port setup
  706.  
  707.         call    RECV_COPY
  708.  
  709.         pop    dx        ; get back port setup
  710.         mov    ax, cs        ;  and set DS back to CODE
  711.         mov    ds, ax
  712.         assume    ds:CODE
  713.  
  714.         ; have to check if 16-bit i/o processed an odd-length packet
  715.         ;  - if so, we MAY have blown the harmony of the 8005 DMA
  716.         ;  (may now be off by one) so just sail it
  717.         test    bl, 1
  718.         jnz    recv_sail_it
  719.  
  720.         pop    cx        ; pop squirreled next HEADER POINTER
  721.         mov    rcv_ptr_copy, cx    ;  and make that the new pointer
  722.         mov    al, ch        ; update the REA PTR register, making
  723.         set_r    REA_PTR_R    ;  room for more inbound packets
  724.         setport    BUFWIN_R
  725.         jmp    recv_loop
  726.  
  727. recv_sail_pop:    add    sp, ENET_HDR    ; pop the Ethernet-2 header from stack
  728.  
  729. recv_sail_it:    ; rid ourselves of this frame the hard way: by resetting the
  730.         ;   DMA, in addition to our own bookkeeping stuff
  731.         loadport
  732.         mov    ax, command_r    ; release the DMA FIFO
  733.         or    ax, FIFO_WRITE+DMA_INT_ACK
  734.         setw_r    COMSTAT_R
  735.         pop    cx        ; pop squirreled next HEADER POINTER
  736.         mov    rcv_ptr_copy, cx    ;  and make that the new pointer
  737.         mov    al, ch        ; update the REA PTR register, making
  738.         set_r    REA_PTR_R    ;  room for more inbound packets
  739.         setport    DMA_ADDR_R
  740.         jmp    recv_rcvptr
  741.  
  742. recv_done:    ; all inbound frames processed
  743.         loadport
  744.         mov    ax, command_r    ; release the DMA FIFO
  745.         or    ax, FIFO_WRITE+DMA_INT_ACK
  746.         setw_r    COMSTAT_R
  747.         ; restart a stalled receiver
  748.         getw_r    COMSTAT_R    ; grab the STATUS register
  749.         test    ax, RX_ON    ; receiver still on?
  750.         jnz    recv_done_1
  751. ifdef    DEBUG
  752.         inc    rx_restart
  753. endif
  754.         call    turn_rx_on
  755. recv_done_1:
  756.         ret
  757.  
  758.  
  759.     public    RECV_EXITING
  760. ; Part two of the ISR receive packet processing.  Called after interrupts
  761. ; have been acknowledged.  Only DS and AX have been saved, DS == CS and
  762. ; we're running on the original stack (i.e. not the private one).  On
  763. ; entry, interrupts are still disabled but it is possible to intelligently
  764. ; turn them back on.
  765. RECV_EXITING:
  766.         assume    ds:CODE
  767.         ret
  768.  
  769.  
  770.     public    RESET_INTERFACE
  771. ; Reset the interface.
  772. RESET_INTERFACE:
  773.         assume    ds:CODE
  774.         loadport
  775.         mov    ax, RESET
  776.         setw_r    CONFIG2_R
  777.         ; wait a good, long while
  778.         mov    ax, 1
  779.         call    set_timeout
  780. RESET_INTERFACE_1:
  781.         call    do_timeout
  782.         jnz    RESET_INTERFACE_1
  783. ;;;;        ; delay another 4 microseconds
  784. ;;;;        io_delay  io_delay_cnt    ; generic delay
  785. ;;;;        io_delay  io_delay_cnt    ; generic delay
  786.         ret
  787.  
  788.  
  789.     private    turn_rx_off
  790. ; Turn off the receiver portion of the device.  Undoes 'turn_rx_on'.
  791. turn_rx_off:
  792.         assume    ds:CODE
  793.         loadport
  794.         mov    ax, command_r
  795.         and    ax, NOT RX_INT_EN
  796.         or    ax, SET_RX_OFF
  797.         mov    command_r, ax
  798.         setw_r    COMSTAT_R
  799.         ret
  800.  
  801.  
  802.     private    turn_rx_on
  803. ; Turn on the receiver portion of the device.  Assumes the station address
  804. ; has already been programmed, receive ISR registered, etc.  Just brings it
  805. ; all to life on the wire.  Undone by 'turn_rx_off'.
  806. turn_rx_on:
  807.         assume    ds:CODE
  808.         loadport
  809.         ; load up Receive End Area Pointer
  810.         mov    al, TEA_INIT + 1
  811.         set_r    REA_PTR_R
  812.         ; load up the Receive Pointer Register
  813.         mov    ax, RCV_PTR
  814.         setw_r    RX_PTR_R
  815.         mov    rcv_ptr_copy, ax
  816.         ; now turn it on
  817.         mov    ax, command_r
  818.         and    ax, NOT SET_RX_OFF
  819.         or    ax, RX_INT_EN
  820.         mov    command_r, ax
  821.         or    ax, SET_RX_ON
  822.         setw_r    COMSTAT_R
  823.         ret
  824.  
  825.  
  826.     public    SEND_PKT
  827. ; Send one packet.  Entry with:
  828. ;    DS:SI    packet buffer to xmit
  829. ;    CX    packet length
  830. ;     ES:DI    upcall routine (0:0 if no upcall is desired) -- only supported
  831. ;        if the high-performance bit is set in 'driver_function' byte
  832. ; Returns:
  833. ;    NC    if OK
  834. ;       or
  835. ;    CY    if transmission error
  836. ;    DH    error code
  837. SEND_PKT:
  838.         assume    ds:nothing
  839. ifdef    DBGINT3
  840.         int    03h        ; Danger, Will Robinson!
  841. endif
  842.         mov    ax, ds        ; restore DS usage right here, saving
  843.         mov    es, ax        ;   it in ES (as we don't do upcalls)
  844.         mov    ax, cs
  845.         mov    ds, ax
  846.         assume     ds:CODE
  847.         cmp    cx, ENET_HDR    ; firewall against trivia
  848.         jae    SEND_PKT_1
  849.         jmp    send_pkt_ret
  850. SEND_PKT_1:
  851.         cmp    cx, GIANT    ; see if too big
  852.         jbe    SEND_PKT_2
  853.         jmp    send_pkt_NOSPACE
  854. SEND_PKT_2:
  855.         cmp    cx, RUNT    ; see if too small
  856.         jae    SEND_PKT_3    ; ... sounds like the Three Bears
  857.         mov    cx, RUNT    ; transmit at least this much
  858. SEND_PKT_3:
  859.         ; mask receiver interrupts (note the receiver is still
  860.         ;   running however) -- this also arbitrates use of the FIFO
  861.         push    cx        ; push stuff so MASKINT won't bite us
  862.         mov    al, INT_NO
  863.         call    MASKINT
  864.  
  865.         loadport
  866.         mov    ax, config1_r_copy    ; select BUFFER MEMORY in BUFWIN
  867.         or    ax, BUFFER_MEM_SEL
  868.         setw_r    CONFIG1_R
  869.  
  870.         ; check status of the previous transmission
  871.         cmp    transmit, 0    ; see if a transmit is incomplete
  872.         je    send_pkt_load
  873. send_pkt_wait:
  874.         loadport        ; synch for the loop
  875.         mov    ax, 3        ; address the TRANSMIT STATUS byte
  876.         setw_r    DMA_ADDR_R
  877.         mov    ax, command_r    ; set the FIFO to READ now, also
  878.         or    ax, FIFO_READ+TX_INT_ACK  ; ACKs any TX int bit
  879.         setw_r    COMSTAT_R
  880.         get_r    BUFWIN_R    ; get the STATUS byte
  881.         mov    bl, al        ; save the gotten STATUS byte
  882.         mov    ax, command_r    ; set the FIFO back to WRITE (aborts,
  883.         or    ax, FIFO_WRITE+DMA_INT_ACK  ; drains, ACKs FIFO int bit)
  884.         setw_r    COMSTAT_R
  885.         test    bl, HDR_DONE    ; check for transmit done
  886. ifdef    DEBUG
  887.         jnz    send_pkt_wait_1
  888.         inc    send_wait_c    ; count send completion waits
  889.         jmp    send_pkt_wait    ; loop
  890. send_pkt_wait_1:
  891. else
  892.         jz    send_pkt_wait    ; loop
  893. endif
  894.         ; done -- check for transmit error & count if so
  895.         test    bl, BABBLE_ERR+COLL_16_ERR
  896.         jz    send_pkt_load
  897.         call    COUNT_OUT_ERR    ; count the error
  898.  
  899. send_pkt_load:    ; load the new packet into the transmit area
  900.         pop    cx        ; revive stuff pushed for MASKINT
  901.         ; load up the transmit buffer -- allows only a single
  902.         ;   transmit (doesn't chain 'em); however, this transmission
  903.         ;   is overlapped with the preparation of the next
  904.         loadport        ; synch the state
  905.         xor    ax, ax        ; TRANSMIT NEXT HEADER word
  906.         setw_r    DMA_ADDR_R
  907.         mov    ax, cx        ; calculate xmit request length
  908.         add    ax, 4        ;   including 8005 garf
  909.         xchg    ah, al        ; SWAP
  910.         setw_r    BUFWIN_R
  911.         mov    al, PACKET_PRESENT+DATA_FOLLOWS ; TRANSMIT COMMAND byte
  912.         xor    ah, ah        ; TRANSMIT STATUS byte
  913.         setw_r    BUFWIN_R    ; both in one SWAPPED swoop
  914.  
  915.         ; semi-quick programmed output here -- direct use of macros
  916.         mov    ax, es        ; go back to DS-relative packet data
  917.         mov    ds, ax
  918.         assume    ds:nothing
  919.         cmp    io_16, 0    ; can do 16-bit i/o?
  920.         je    send_pkt_8bit
  921.         test    cx, 1        ; odd length?
  922.         jz    send_pkt_load_1
  923.         inc    cx        ; make even for 16-bit mode output
  924. send_pkt_load_1:
  925.         sar    cx, 1        ; /2: compute # of words to transfer
  926.         cmp    iowm_delay_cnt, 0  ; check for full-tilt boogie
  927.         jne    send_pkt_load_2    ; nope, need delays
  928.         rep    outsw        ; blow-out clearance i/o
  929.         jmp    SHORT send_pkt_xmt
  930. send_pkt_load_2:
  931.         mov    ax, [si]    ; Note: DS-relative packet here
  932.         out    dx, ax
  933.         io_delay iowm_delay_cnt    ; reduced delay w/i loop
  934.         inc    si
  935.         inc    si
  936.         loop    send_pkt_load_2
  937.         jmp    SHORT send_pkt_xmt    ; done w/ buffer load
  938.  
  939. send_pkt_8bit:    ; 8 bit packet load
  940.         cmp    iowm_delay_cnt, 0  ; check for full-tilt boogie
  941.         jne    UpHere_1    ; nope, need delays
  942.  
  943. ;the following replaces rep outsb, which doesn't work too well on 8088-based PCs
  944. ;;    rep    outsb            ; blow-out clearance i/o
  945.  
  946. UpHere:        lodsb            ; Note: DS-relative packet here
  947.         out    dx, al
  948.                 loop    UpHere
  949.  
  950.         jmp    SHORT send_pkt_xmt
  951. UpHere_1:
  952.         mov    al, [si]    ; Note: DS-relative packet here
  953.         out    dx, al
  954.         io_delay iowm_delay_cnt    ; reduced delay w/i loop
  955.         inc    si
  956.         loop    UpHere_1
  957.  
  958. send_pkt_xmt:    ; packet now loaded, finish up and transmit it
  959.         mov    ax, cs        ; restore our DS
  960.         mov    ds, ax
  961.         assume    ds:CODE
  962.         ; set TRANSMIT POINTER (overlap as FIFO drains)
  963.         xor    ax, ax        ; start chain at address 0
  964.         setw_r    TX_PTR_R
  965.         ; wait for DMA FIFO to drain
  966.         fifo_wait
  967.         ; now transmit it
  968.         mov    ax, command_r    ; remove default transmitter off cmd
  969.         and    ax, NOT SET_TX_OFF
  970.         mov    command_r, ax
  971.         or    ax, SET_TX_ON
  972.         setw_r    COMSTAT_R    ; Send!
  973.         mov    transmit, 1    ; note to check completion next time
  974.         ; unmask receiver interrupts, releasing FIFO
  975.         mov    al, INT_NO
  976.         call    UNMASKINT
  977. send_pkt_ret:
  978.         clc            ; OK
  979.         ret
  980.  
  981. send_pkt_NOSPACE:
  982.         mov    dh, NO_SPACE
  983.         stc            ; error
  984.         ret
  985.  
  986.  
  987.     public    SET_ADDRESS
  988. ; Set the MAC station receiver address.  Entry with:
  989. ;    DS:SI    the new MAC address to adopt
  990. ;    CX    length of address at DS:SI
  991. ; Returns:
  992. ;    DS    restored to point at CODE segment
  993. ;       plus
  994. ;    NC    if OK
  995. ;       or
  996. ;    CY    if error
  997. ;    DH    error code
  998. SET_ADDRESS:
  999.         assume    ds:nothing
  1000.         mov    ax, ds        ; undo the twisted DS usage
  1001.         mov    es, ax        ;   back to addressing our memory
  1002.         mov    ax, cs
  1003.         mov    ds, ax
  1004.          assume    ds:CODE
  1005.         cmp    cx, EADDR_LEN    ; check for proper address length
  1006.         jne    set_addr_BADADDRESS
  1007.         test    BYTE PTR es:[si], 1  ; deny broadcast or multicast
  1008.         jnz    set_addr_BADADDRESS
  1009.  
  1010. ;#        ; set the new station address (JLD)(9-25-91)
  1011.                 mov     ax, ds:[si][0]
  1012.                 mov     word ptr ds:station_0[0], ax
  1013.  
  1014.                 mov     ax, ds:[si][2]
  1015.                 mov     word ptr ds:station_0[2], ax
  1016.  
  1017.                 mov     ax, ds:[si][4]
  1018.                 mov     word ptr ds:station_0[4], ax
  1019.  
  1020.         ; disable receiver and/or transmitter
  1021.         call    rcvxmt_off    ; leaves COMMAND register on stack
  1022.  
  1023.         ; set the new station address
  1024.         loadport
  1025.         mov    ax, config1_r_copy    ; select STATION address reg 0
  1026.         or    ax, STATION_0_SEL
  1027.         setw_r    CONFIG1_R
  1028.         xor    ax, ax
  1029.         setw_r    DMA_ADDR_R
  1030.         setport    BUFWIN_R    ; fix access within the loop
  1031. SET_ADDRESS_1:
  1032.         mov    al, es:[si]    ; output bytes of the new address
  1033.         inc    si
  1034.         set_r    BUFWIN_R
  1035.         loop    SET_ADDRESS_1
  1036.  
  1037.         ; reenable the receiver if it was on before
  1038.         call    rcv_on        ; removes COMMAND register from stack
  1039.  
  1040.         clc            ; OK
  1041.         ret
  1042.  
  1043. set_addr_BADADDRESS:
  1044.         mov    dh, BAD_ADDRESS    ; improper address
  1045.         stc            ; error
  1046.         ret
  1047.  
  1048.  
  1049.     public    set_multicast_list
  1050. set_multicast_list:
  1051. ;enter with ds:si ->list of multicast addresses, ax = number of addresses,
  1052. ;  cx = number of bytes.
  1053.         assume    ds:CODE
  1054.         jmp    SHORT set_multicast_err    ; FOR NOW: no multicast support
  1055.         clc            ; OK
  1056.         ret
  1057. set_multicast_err:
  1058.         mov    dh, NO_MULTICAST
  1059.         stc            ; error
  1060.         ret
  1061.  
  1062.  
  1063.     public    TERMINATE
  1064. ; Terminate the packet driver.  Nothing special to do.
  1065. TERMINATE:
  1066.         assume    ds:CODE
  1067.         ret
  1068.  
  1069.     public    XMIT
  1070. ; Routine to process a queued transmission with the least possible latency.
  1071. ; Called immediately within the receive ISR.  The attempt here is to effect
  1072. ; back-to-back transmissions.  This routine may only use AX and DX freely
  1073. ; and is running whatever stack exists at interrupt time (not the private
  1074. ; one).  This routine isn't necessary on the 8005, because it can chain
  1075. ; transmissions (if anyone really cared).
  1076. XMIT:
  1077.         assume    ds:nothing
  1078.         ret
  1079.  
  1080.  
  1081.     include    popf.asm
  1082.     include timeout.asm
  1083.  
  1084.  
  1085. ; Everything above this line is resident upon successful load.
  1086.  
  1087.     public    timer_isr
  1088. timer_isr:
  1089. ;if the first instruction is an iret, then the timer is not hooked
  1090.     iret
  1091.  
  1092. ;any code after this will not be kept.  Buffers used by the program, if any,
  1093. ;are allocated from the memory between end_resident and end_free_mem.
  1094.     public end_resident,end_free_mem
  1095. end_resident    label    byte
  1096. end_free_mem    label    byte
  1097.  
  1098. ; Everything below this line is discarded upon installation.
  1099.  
  1100.     public    USAGE_MSG
  1101. USAGE_MSG    db    "usage: davidsys [options] <packet_int_no> <hardware_irq> <io_addr> <delay_mult>",CR,LF,'$'
  1102.  
  1103.     public    COPYRIGHT_MSG
  1104. COPYRIGHT_MSG    db    "Packet driver for the Ether-T PC/AT ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,".",'0'+s8005_version,CR,LF
  1105.         db    "Portions Copyright 1991 DAVID Systems, Inc and Marc S Dye",CR,LF,'$'
  1106.  
  1107. badint_msg    db    "Specified device interrupt not supported for"
  1108.         db    " your installation",CR,LF,'$'
  1109. int8_msg    db    " Try: 2, 3, 4, 5, 6, or 7",CR,LF,'$'
  1110. int16_msg    db    " Try: 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, or 15"
  1111.         db      CR,LF,'$'
  1112. inttimedout_msg    db    "Timed out awaiting device test interrupt",CR,LF,'$'
  1113. nodev_msg    db    "No device found at specified i/o address",CR,LF,'$'
  1114. setaddr_msg    db    "Unable to set initial station address",CR,LF,'$'
  1115.  
  1116. delay_mult_name    db    "Delay multipler (x10) ",'$'
  1117. int_no_name    db    "Interrupt number ",'$'
  1118. io_addr_name    db    "I/O port ",'$'
  1119.  
  1120. ifdef    DEBUG
  1121. count_del_name    db    "Incremental i/o delay spin (nsec x10) ",'$'
  1122. delay_del_name    db    "Basic i/o delay (nsec x100) ",'$'
  1123. io_delay_name    db    "I/O delay (spins) ",'$'
  1124. iowm_delay_name    db    "I/O write memory delay (spins) ",'$'
  1125. null_bias_name    db    "Null (overhead) cost (nsec x100) ",'$'
  1126. null_spin_name    db    "Null spin count ",'$'
  1127. ref_spin_name    db    "Reference spin count ",'$'
  1128. unit_spin_name    db    "Single spin count ",'$'
  1129. endif
  1130.  
  1131. ;-> the assigned Ethernet address of the card.
  1132.     extrn    rom_address: byte
  1133.  
  1134.     private    int_map
  1135. ; Interrupt number to Ether-T PC/AT configuration register code mapping table
  1136.         ;     0    1    2    3    4    5    6    7
  1137. int_map        db    00h, 00h, 02h, 01h, 03h, 04h, 05h, 06h
  1138.         ;     8    9   10   11   12   13   14   15
  1139.         db    00h, 02h, 07h, 08h, 09h, 00h, 0Ah, 0Bh
  1140.  
  1141. REF_DELAY    equ    64
  1142.  
  1143.     private    count_delay
  1144. count_delay    dw    ?, 0        ; # of 10 nsecs in each io_delay spin
  1145.     private    delay_delay
  1146. delay_delay    dw    ?, 0        ; # of 100 nsecs in the basic io_delay
  1147.     private    null_bias
  1148. null_bias    dw    ?, 0        ; # of 100 nsecs in a null spin
  1149.     private    null_spins
  1150. null_spins    dw    ?, 0        ; # of spins for without io_delay macro
  1151.     private    ref_spins
  1152. ref_spins    dw    ?, 0        ; # of spins for io_delay of 1+REF_DELAY
  1153.     private    test_delay
  1154. test_delay    dw    ?        ; holding tank for current sample count
  1155.     private    unit_spins
  1156. unit_spins    dw    ?, 0        ; # of spins for io_delay of 1
  1157.  
  1158.  
  1159. ; macro to sample timer zero's counter register
  1160. timer0_sample    macro
  1161.         xor    al, al        ; latch timer zero counter 
  1162.         out    43h, al
  1163.         in    al, 40h        ; sample timer zero counter
  1164.         mov    ah, al
  1165.         in    al, 40h
  1166.         xchg    ah, al
  1167.         endm
  1168.  
  1169.  
  1170.     private    calibrate_one
  1171. calibrate_one:
  1172.         assume    ds:CODE
  1173.         mov    test_delay, ax
  1174.         ; find the timer near zero
  1175.         mov    cx, 100h
  1176. calibrate_one_1:
  1177.         timer0_sample
  1178.         cmp    ax, cx
  1179.         ja    calibrate_one_1
  1180.         ; now find the timer above that value (after wrapping to max)
  1181. calibrate_one_2:
  1182.         timer0_sample
  1183.         cmp    ax, cx
  1184.         jbe    calibrate_one_2
  1185.         ; wait till it gets to the one quarter point
  1186.         mov    cx, 0C000h
  1187. calibrate_one_3:
  1188.         timer0_sample
  1189.         cmp    ax, cx
  1190.         ja    calibrate_one_3
  1191.         ; calibration time - disable interrupts, watch for half a
  1192.         ;  clock period to elapse
  1193.         pushf
  1194.         cli
  1195.         xor    bx, bx
  1196.         mov    cx, 4000h
  1197.         cmp    test_delay, 0        ; see if measuring overhead?
  1198.         jne    calibrate_io
  1199.         ; calibrating the overhead of this timing stuff only
  1200. calibrate_one_4:
  1201.         inc    bx
  1202.         jc    calibrate_exit        ; overflow - exit w/ 0
  1203.         timer0_sample
  1204.         cmp    ax, cx
  1205.         ja    calibrate_one_4
  1206.         jmp    SHORT calibrate_exit
  1207. calibrate_io:    io_delay  test_delay
  1208.         inc    bx
  1209.         jc    calibrate_exit        ; overflow - exit w/ 0
  1210.         timer0_sample
  1211.         cmp    ax, cx
  1212.         ja    calibrate_io
  1213. calibrate_exit:    popf
  1214.         mov    ax, bx
  1215.         ret
  1216.  
  1217.  
  1218.     private    calibrate_delay
  1219. calibrate_delay:
  1220.         assume    ds:CODE
  1221.         ; if user wanted no delays, skip all this stuff
  1222.         cmp    delay_mult, 0
  1223.         jne    calibrate_delay_1
  1224.         xor    ax, ax            ; normalize to zero
  1225.         jmp    calibrate_store
  1226. calibrate_delay_1:
  1227.         ; calibrate the overhead of the time sampling loop
  1228.         xor    ax, ax
  1229.         call    calibrate_one
  1230.         or    ax, ax
  1231.         jz    calibrate_dj        ; can't time -- default it all
  1232.         mov    null_spins, ax
  1233. ifdef    DEBUG
  1234.         mov    di, OFFSET null_spins
  1235.         mov    dx, OFFSET null_spin_name
  1236.         call    PRINT_NUMBER
  1237. endif
  1238.         ; calibrate a unit (single spin) loop
  1239.         mov    ax, 1
  1240.         call    calibrate_one
  1241.         or    ax, ax
  1242.         jz    calibrate_dj        ; can't time -- default it
  1243.         mov    unit_spins, ax
  1244. ifdef    DEBUG
  1245.         mov    di, OFFSET unit_spins
  1246.         mov    dx, OFFSET unit_spin_name
  1247.         call    PRINT_NUMBER
  1248.         mov    ax, unit_spins    ; reload AX
  1249. endif
  1250.         cmp    null_spins, ax        ; ensure null >= unit
  1251.         jb    calibrate_dj        ; bogonic -- default it
  1252.         ; now one with some teeth in it
  1253.         mov    ax, 1+REF_DELAY
  1254.         call    calibrate_one
  1255.         or    ax, ax
  1256.         jz    calibrate_dj        ; can't time -- default it all
  1257.         mov    ref_spins, ax
  1258. ifdef    DEBUG
  1259.         mov    di, OFFSET ref_spins
  1260.         mov    dx, OFFSET ref_spin_name
  1261.         call    PRINT_NUMBER
  1262.         mov    ax, ref_spins        ; reload AX
  1263. endif
  1264.         cmp    unit_spins, ax        ; ensure unit > ref
  1265.         ja    calibrate_dj_1
  1266. calibrate_dj:    jmp    SHORT calibrate_def    ; bogonic -- default it
  1267. calibrate_dj_1:
  1268.  
  1269.         ; now do the math - compute the overhead of the timing code
  1270.         ;  (in units of 100 nsec)
  1271.         mov    cx, null_spins
  1272.         call    compute_100nsecs
  1273.         jc    calibrate_def
  1274.         mov    null_bias, ax
  1275. ifdef    DEBUG
  1276.         mov    di, OFFSET null_bias
  1277.         mov    dx, OFFSET null_bias_name
  1278.         call    PRINT_NUMBER
  1279. endif
  1280.         ; compute cost of the basic i/o delay macro (in units of
  1281.         ;  100 nsec)
  1282.         mov    cx, unit_spins
  1283.         call    compute_100nsecs
  1284.         jc    calibrate_def
  1285.         sub    ax, null_bias
  1286.         mov    delay_delay, ax
  1287. ifdef    DEBUG
  1288.         mov    di, OFFSET delay_delay
  1289.         mov    dx, OFFSET delay_del_name
  1290.         call    PRINT_NUMBER
  1291. endif
  1292.         ; compute cost of the reference spin in the i/o delay
  1293.         ;  macro (this time, in units of 10 nsec)
  1294.         mov    cx, ref_spins
  1295.         call    compute_100nsecs
  1296.         jc    calibrate_def
  1297.         sub    ax, null_bias
  1298.         sub    ax, delay_delay        ; now delay for REF_COUNT spins
  1299.         mov    cx, 10
  1300.         mul    cx
  1301.         jnc    calibrate_dj_2
  1302.         ; this machine is *sooo* slow, a minimum delay is fine
  1303.         mov    ax, 1
  1304.         jmp    SHORT calibrate_store
  1305. calibrate_dj_2:
  1306.         mov    cx, REF_DELAY
  1307.         div    cx
  1308.         mov    count_delay, ax
  1309. ifdef    DEBUG
  1310.         mov    di, OFFSET count_delay
  1311.         mov    dx, OFFSET count_del_name
  1312.         call    PRINT_NUMBER
  1313. endif
  1314.  
  1315.         ; now, see if basic i/o delay (w/o extra spins) is enough
  1316.         mov    bx, 1            ; basic macro includes one spin
  1317.         mov    ax, delay_delay
  1318.         sub    ax, 22            ; >= 2.2 usec ?
  1319.         jae    calibrate_dj_3
  1320.         neg    ax            ; correct sign of residual
  1321.         mov    cx, 10            ; scale to 10 nsec units
  1322.         mul    cx
  1323.         jc    calibrate_def
  1324.         div    count_delay
  1325.         inc    ax
  1326.         add    bx, ax            ; back into spin count result
  1327. calibrate_dj_3:
  1328.         mov    ax, bx            ; the calibrated basic result
  1329.         jmp    SHORT calibrate_store
  1330.  
  1331. calibrate_def:    ; this processor is *sooo* fast that we can't calibrate it!?
  1332.         ; just use a conservative default (should be good to about a
  1333.         ;  400MHz 80486)
  1334.         mov    ax, 120
  1335.  
  1336.         ; scale the delay by the user-request multiplier (if any)
  1337. calibrate_store:
  1338.         mul    delay_mult        ; a tens multiple
  1339.         mov    cx, 10            ; so reduce / 10
  1340.         div    cx            ; allowed to be zero
  1341.         mov    io_delay_cnt, ax
  1342.         ; compute the .9 usec 'iowm_delay_cnt'
  1343.         mov    cx, 9
  1344.         mul    cx
  1345.         mov    cx, 20
  1346.         div    cx
  1347.         mov    iowm_delay_cnt, ax
  1348.         ret
  1349.  
  1350.  
  1351.     private    compute_100nsecs
  1352. ; Given a value in CX (representing spin counter during a 1/36.04 timing
  1353. ;  run), compute the number of hundreds of nanoseconds per unit spin. Return
  1354. ;  result in AX, or set CY and return if algorithm won't scale.
  1355. compute_100nsecs:
  1356.         assume    ds:CODE
  1357.         mov    ax, 9            ; compute spins * 9 (36.04/4)
  1358.         mul    cx
  1359.         jc    compute_100n_err    ; won't fit -- return error
  1360.         mov    cx, ax            ; prepare for division
  1361.         mov    dx, 26h            ; put 2,500,000 into DX,AX
  1362.         mov    ax, 25a0h
  1363.         div    cx            ; compute 10M / (spins*36.04)
  1364.         clc
  1365.         ret
  1366. compute_100n_err:
  1367.         ret
  1368.  
  1369.  
  1370.     public    ETOPEN
  1371. ; Perform initial open of the device.  Called only once: immediately prior to
  1372. ; hooking the packet driver service interrupt and TSRing.  Device interrupts
  1373. ; are to be hooked (if this routine deems reasonable to do so).  No registers
  1374. ; are given on entry.  On return:
  1375. ;    NC    if OK
  1376. ;    DX    offset of the end of the resident portion of the driver
  1377. ;       or
  1378. ;    CY    if error
  1379. ETOPEN:
  1380.         assume    ds:CODE
  1381. ifdef    DBGINT3
  1382.         int    03h        ; Danger, Will Robinson!
  1383. endif
  1384.         ; calibrate the i/o delays needed
  1385.         call    calibrate_delay
  1386. ifdef    DEBUG
  1387.         mov    di, OFFSET io_delay_cnt
  1388.         mov    dx, OFFSET io_delay_name
  1389.         call    PRINT_NUMBER
  1390.         mov    di, OFFSET iowm_delay_cnt
  1391.         mov    dx, OFFSET iowm_delay_name
  1392.         call    PRINT_NUMBER
  1393. endif
  1394.  
  1395.         ; test for access to the device; check for 16-bit i/o
  1396.         call    try_io
  1397.         jnc    ETOPEN_1
  1398.         jmp    etopen_err
  1399. ETOPEN_1:
  1400.         ; try out the interrupt channel & leave hooked if successful
  1401.         call    try_interrupt
  1402.         jnc    ETOPEN_2
  1403.         jmp    etopen_err
  1404. ETOPEN_2:
  1405.         ; get the PROM Ethernet address
  1406.         call    RESET_INTERFACE    ; reset the device (again!)
  1407.         loadport
  1408.         mov    ax, command_r    ; everything still off
  1409.         or    ax, FIFO_WRITE
  1410.         setw_r    COMSTAT_R
  1411.         xor    ax, ax        ; reset the DMA address to 0
  1412.         setw_r    DMA_ADDR_R
  1413.         mov    ax, config1_r_copy    ; select the PROM
  1414.         or    ax, PROM_SEL
  1415.         setw_r    CONFIG1_R
  1416.         mov    ax, config2_r_copy    ; blast the watchdog
  1417.         or    ax, KILL_WATCHDOG
  1418.         setw_r    CONFIG2_R
  1419.         push    ds
  1420.         pop    es
  1421.         mov    di,offset rom_address
  1422.         setport    BUFWIN_R    ; fix access within the loop
  1423.         mov    cx, EADDR_LEN    ; allocate some space for the address
  1424. ETOPEN_3:
  1425.         get_r    BUFWIN_R    ; next byte of PROM Ethernet address
  1426.         stosb
  1427.         loop    ETOPEN_3
  1428.  
  1429.         ; set the PROM address as the default STATION 0 address
  1430.         call    RESET_INTERFACE    ; reset the device (yet again!)
  1431.  
  1432.         mov    cx, EADDR_LEN    ; size of Ethernet address
  1433.         mov    si, offset rom_address
  1434.         call    SET_ADDRESS
  1435.  
  1436.         ; load up Transmit End Area Pointer
  1437.         loadport
  1438.         mov    ax, config1_r_copy
  1439.         or    ax, TEA_SEL
  1440.         setw_r    CONFIG1_R
  1441.         mov    al, TEA_INIT
  1442.         set_r    BUFWIN_R
  1443.  
  1444.         ; set up a few other default goodies once only here:
  1445.         ;  config 1 :    STATION 0 address (Matchmode by 'rcv_mode_3')
  1446.         ;  config 2 :    defaults are OK
  1447.         or    config1_r_copy, STATION_0_EN
  1448.         mov    ax, config1_r_copy
  1449.         setw_r    CONFIG1_R
  1450.  
  1451.         ; we're go now -- wake up and smell the roses!
  1452.         and    command_r, NOT SET_RX_OFF
  1453.         ; reload the CARD CONFIGURATION register
  1454.         mov    ax, config1_r_copy    ; select CARD CONFIGURATION register
  1455.         or    ax, CARD_CONFIG_SEL
  1456.         setw_r    CONFIG1_R
  1457.         mov    al, cardconfig_r
  1458.         set_r    BUFWIN_R    ; writes CARD CONFIGURATION register
  1459.         ; turn on the receiver now, in (default) mode 3
  1460.         call    rcv_mode_3
  1461.  
  1462.         clc            ; OK
  1463.         ret
  1464.  
  1465. etopen_err:
  1466.         stc            ; error
  1467.         ret
  1468.  
  1469.  
  1470.     public    PARSE_ARGS
  1471. ; Parse the device-specific portion of the invokation command line (after
  1472. ; the packet driver vector number).  Entry with:
  1473. ;    DS:SI    residual command line
  1474. ; Can emit device-specific bitches as necessary (using DOS i/o) and exit
  1475. ; without returning.  If it returns:
  1476. ;    NC    if OK
  1477. ;       or
  1478. ;    CY    if usage error
  1479. ; Returning w/ carry set will cause a usage error, then exit without further
  1480. ; processing.
  1481. PARSE_ARGS:
  1482.         assume    ds:CODE
  1483.         mov    di, OFFSET INT_NO
  1484.         call    GET_NUMBER
  1485.         mov    di, OFFSET IO_ADDR
  1486.         call    GET_NUMBER
  1487.         mov    di, OFFSET delay_mult
  1488.         call    GET_NUMBER
  1489.         clc            ; OK
  1490.         ret
  1491.  
  1492.  
  1493.     public    PRINT_PARAMETERS
  1494. ; Spew out all of the configuration parameters to the console device.
  1495. ; Called within the main program, just prior to deciding to TSR.  Should
  1496. ; dump out each device-specific parameter, parsed by PARSE_ARGS (just to
  1497. ; make the user warm-and-fuzzy I suppose...)  Entry with:
  1498. ;    no special register contents
  1499. ; Returns:
  1500. ;    nothing
  1501. PRINT_PARAMETERS:
  1502.         assume    ds:CODE
  1503.         mov    di, OFFSET INT_NO
  1504.         mov    dx, OFFSET int_no_name
  1505.         call    PRINT_NUMBER
  1506.         mov    di, OFFSET IO_ADDR
  1507.         mov    dx, OFFSET io_addr_name
  1508.         call    PRINT_NUMBER
  1509.         mov    di, OFFSET delay_mult
  1510.         mov    dx, OFFSET delay_mult_name
  1511.         call    PRINT_NUMBER
  1512.         ret
  1513.  
  1514.  
  1515.     private    try_interrupt
  1516. ; try out the receiver interrupt (once at the outset); in the process,
  1517. ; sets up the CARD CONFIGURATION register
  1518. try_interrupt:
  1519.         assume ds:CODE
  1520.         call    RESET_INTERFACE
  1521.         loadport
  1522.         mov    ax, config1_r_copy    ; program CARD CONFIGURATION register
  1523.         or    ax, CARD_CONFIG_SEL
  1524.         setw_r    CONFIG1_R
  1525.         ; map interrupt number to card config enumeration value
  1526.         mov    bx, WORD PTR INT_NO
  1527.         mov    al, [bx + OFFSET int_map]
  1528.         or    al, al
  1529.         jz    try_int_badint_err
  1530.         ; check for 16-bit i/o usage; include in card config value
  1531.         cmp    io_16, 0
  1532.         je    try_interrupt_1
  1533.         or    al, SET_16BITMODE_M
  1534. try_interrupt_1:
  1535.         set_r    BUFWIN_R    ; writes CARD CONFIGURATION register
  1536.         mov    cardconfig_r, al  ; save soft-copy of this
  1537.         ; install the 'recv_isr' handler
  1538.         mov    int_test, 1    ; flag that we're testing only
  1539.         call    SET_RECV_ISR
  1540.         ; cause a receiver interrupt
  1541.         loadport
  1542.         mov    ax, command_r    ; SET_RX_OFF in here now
  1543.         or    ax, RX_INT_EN+SET_RX_ON
  1544.         setw_r    COMSTAT_R    ; Interrupt!
  1545. ;# Compaq DOS leaves CPU ints disabled!
  1546.                 sti
  1547.         mov    ax, 1        ; wait one tick
  1548.         call    set_timeout
  1549. try_interrupt_2:
  1550.         call    do_timeout
  1551.         jz    try_int_timedout
  1552.         cmp    int_tested, 0    ; did it happen?
  1553.         je    try_interrupt_2
  1554.         ; the interrupt worked -- leave w/ the receiver off, the
  1555.         ;   ISR hooked but masked
  1556.         mov    ax, command_r    ; SET_RX_OFF in here now
  1557.         setw_r    COMSTAT_R    ; shut up!
  1558.         mov    al, INT_NO
  1559.         call    MASKINT
  1560.         mov    int_test, 0    ; from now on, it's the real thing
  1561.         clc            ; OK
  1562.         ret
  1563.  
  1564. try_int_timedout:
  1565.         ; timed-out (bummer dude!) -- turn receiver back off
  1566.         mov    ax, command_r    ; SET_RX_OFF in here now
  1567.         or    ax, RX_INT_ACK     ; just in case ...
  1568.         setw_r    COMSTAT_R    ; shut up!
  1569.         ; deregister handler and boogie
  1570.         ;   This should be a standard call -- I've asked Russ ...
  1571.         mov    al, INT_NO
  1572.         add    al, 8
  1573.         cmp    al, 8+8        ; is it a slave 8259 interrupt?
  1574.         jb    try_int_timedout_1; no.
  1575.         add    al, 70h - (8+8)    ; map it to the real interrupt
  1576. try_int_timedout_1:
  1577.         push    ds
  1578.         assume    ds:nothing
  1579.         lds    dx, THEIR_ISR
  1580.         mov    ah, 25h
  1581.         int    21h
  1582.         pop    ds
  1583.         assume    ds:CODE
  1584.         mov    dx, OFFSET inttimedout_msg    
  1585.         jmp    SHORT try_int_err
  1586. try_int_badint_err:
  1587.         mov    dx, OFFSET badint_msg
  1588.         mov    ah, 9        ; blab to the user
  1589.         int    21h
  1590.         cmp    io_16, 0    ; tell him/her/it what's legit
  1591.         jne    try_int_badint_err_1
  1592.         mov    dx, OFFSET int8_msg
  1593.         jmp    SHORT try_int_err
  1594. try_int_badint_err_1:
  1595.         mov    dx, OFFSET int16_msg
  1596. try_int_err:
  1597.         stc            ; error
  1598.         ret
  1599.  
  1600.  
  1601.     private    try_io
  1602. ; try out i/o to the device; in the process, determines if 16-bit i/o is OK
  1603. ; NOTE: This code was transliterated from the C source for DAVID's NDIS driver.
  1604. ;   In the absence of any written documentation on the CARD CONFIGURATION
  1605. ;   register, the many magic numbers herein remain a mystery.
  1606. try_io:
  1607.         assume ds:CODE
  1608.         call    RESET_INTERFACE
  1609.         mov    command_r, SET_TX_OFF+SET_RX_OFF+SET_DMA_OFF
  1610.         loadport
  1611.         mov    ax, config1_r_copy    ; select CARD CONFIGURATION register
  1612.         or    ax, CARD_CONFIG_SEL
  1613.         set_r    CONFIG1_R    ; BYTE-size only here!
  1614.         ; first taunting of the device ...
  1615.         mov    al, 05h        ; C sez: "4 bit is LSBS value"
  1616.         set_r    BUFWIN_R    ;        " ... and ... "
  1617.         get_r    BUFWIN_R    ; C sez: "comes back in the 4 MSBs"
  1618.         and    al, 0F0h
  1619.         cmp    al, 50h        ; this has to compare
  1620.         jne    try_io_err
  1621.         ; second taunting of the device ...
  1622.         mov    al, 0Ah        ; "Your mother was a hamster ..."
  1623.         set_r    BUFWIN_R
  1624.         get_r    BUFWIN_R    ; "... and your father smelt of"
  1625.         and    al, 0F0h    ;     "ELDERBERRIES!"
  1626.         cmp    al, 0A0h
  1627.         jne    try_io_err
  1628.         ; now for something completely different -- ask the CARD
  1629.         ;   CONFIGURATION register if it's plugged into an 8- or
  1630.         ;   16-bit slot; mark it well
  1631.         get_r    BUFWIN_R
  1632.         and    al, 1        ; bit on means 8-bit slot
  1633.         xor    al, 1        ; complement sense for 'io_16'
  1634.         mov    io_16, al
  1635.         jz    try_io_1
  1636.         ; set-up 16-bit versions of word i/o functions
  1637.         ; N.B. From this point forward, we MUST to 16 bit i/o
  1638.         ;   on 16 bit objects if the card told us to!
  1639.         mov    inw_fn, OFFSET inw_8005   ; input function
  1640.         mov    outw_fn, OFFSET outw_8005 ; output function
  1641. try_io_1:
  1642.         clc            ; OK
  1643.         ret
  1644.  
  1645. try_io_err:
  1646.         mov    dx, OFFSET nodev_msg
  1647.         stc            ; error
  1648.         ret    ; "Now go away or I shall taunt you a second time-uh!"
  1649.  
  1650.  
  1651. CODE        ends
  1652.  
  1653.         end
  1654.  
  1655.  
  1656. ; eof
  1657.