home *** CD-ROM | disk | FTP | other *** search
/ Stars of Shareware: Programmierung / SOURCE.mdf / programm / msdos / asm / drivers1 / head.asm < prev    next >
Assembly Source File  |  1992-01-14  |  34KB  |  1,300 lines

  1.     include    defs.asm
  2.  
  3. ;  Copyright, 1988-1992, Russell Nelson, Crynwr Software
  4.  
  5. ;   This program is free software; you can redistribute it and/or modify
  6. ;   it under the terms of the GNU General Public License as published by
  7. ;   the Free Software Foundation, version 1.
  8. ;
  9. ;   This program is distributed in the hope that it will be useful,
  10. ;   but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. ;   GNU General Public License for more details.
  13. ;
  14. ;   You should have received a copy of the GNU General Public License
  15. ;   along with this program; if not, write to the Free Software
  16. ;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. code    segment word public
  19.     assume    cs:code, ds:code
  20.  
  21.     public    phd_environ
  22.     org    2ch
  23. phd_environ    dw    ?
  24.  
  25.     public    phd_dioa
  26.     org    80h
  27. phd_dioa    label    byte
  28.  
  29.     org    100h
  30. start:
  31.     jmp    start_1
  32.     extrn    start_1: near
  33.     even                ;put the stack on a word boundary.
  34.  
  35. ;we use our dioa for a stack space.  Very hard usage has shown that only
  36. ;  27 bytes were being used, so 128 should be sufficient.
  37. our_stack    label    byte
  38.  
  39.  
  40.     extrn    int_no: byte
  41.     public    packet_int_no, is_at, sys_features, flagbyte
  42. packet_int_no    db    ?,?,?,?        ; interrupt to communicate.
  43. is_at        db    0        ; =1 if we're on an AT.
  44. sys_features    db    0        ; 2h = MC   40h = 2nd 8259
  45. flagbyte    db    0
  46.     even
  47.  
  48. functions    label    word
  49.     dw    f_not_implemented    ;0
  50.     dw    f_driver_info        ;1
  51.     dw    f_access_type        ;2
  52.     dw    f_release_type        ;3
  53.     dw    f_send_pkt        ;4
  54.     dw    f_terminate        ;5
  55.     dw    f_get_address        ;6
  56.     dw    f_reset_interface    ;7
  57.     dw    f_stop            ;8
  58.     dw    f_not_implemented    ;9
  59.     dw    f_get_parameters    ;10
  60.     dw    f_not_implemented    ;11
  61.     dw    f_as_send_pkt        ;12
  62.     dw    f_drop_pkt        ;13
  63.     dw    f_not_implemented    ;14
  64.     dw    f_not_implemented    ;15
  65.     dw    f_not_implemented    ;16
  66.     dw    f_not_implemented    ;17
  67.     dw    f_not_implemented    ;18
  68.     dw    f_not_implemented    ;19
  69.     dw    f_set_rcv_mode        ;20
  70.     dw    f_get_rcv_mode        ;21
  71.     dw    f_set_multicast_list    ;22
  72.     dw    f_get_multicast_list    ;23
  73.     dw    f_get_statistics    ;24
  74.     dw    f_set_address        ;25
  75.  
  76.     extrn    driver_class: byte
  77.     extrn    driver_type: byte
  78.     extrn    driver_name: byte
  79.     extrn    driver_function: byte
  80.     extrn    parameter_list: byte
  81.  
  82.     extrn    send_pkt: near
  83.     extrn    as_send_pkt: near
  84.     extrn    drop_pkt: near
  85.     extrn    get_address: near
  86.     extrn    set_address: near
  87.     extrn    terminate: near
  88.     extrn    reset_interface: near
  89.     extrn    xmit: near
  90.     extrn    recv: near
  91.     extrn    recv_exiting: near
  92.     extrn    etopen: near
  93.  
  94.     extrn    rcv_modes: word        ;count of modes followed by mode handles.
  95.  
  96.     extrn    set_multicast_list: near
  97.  
  98. linc    macro    n            ; inc a 32 bit integer
  99.     local    a
  100.     inc    n            ;increment the low word
  101.     jne    a            ;go if not overflow
  102.     inc    n+2            ;increment the high word
  103. a:
  104.     endm
  105.  
  106. per_handle    struc
  107. in_use        db    0        ;non-zero if this handle is in use.
  108. packet_type    db    MAX_P_LEN dup(0);associated packet type.
  109. packet_type_len    dw    0        ;associated packet type length.
  110. receiver    dd    0        ;receiver handler.
  111. receiver_sig    db    8 dup(?)    ;signature at the receiver handler.
  112. class        db    ?        ;interface class
  113. per_handle    ends
  114.  
  115. handles        per_handle MAX_HANDLE dup(<>)
  116. end_handles    label    byte
  117.  
  118.     public    multicast_count, multicast_addrs, multicast_broad
  119. multicast_count    dw    0        ;count of stored multicast addresses.
  120. multicast_broad    db    0ffh,0ffh,0ffh,0ffh,0ffh,0ffh    ; entry for broadcast
  121. multicast_addrs    db    MAX_MULTICAST*EADDR_LEN dup(?)
  122.  
  123. have_my_address    db    0        ;nonzero if our address has been set.
  124. my_address    db    MAX_ADDR_LEN dup(?)
  125. my_address_len    dw    ?
  126.  
  127. rcv_mode_num    dw    3
  128.  
  129. free_handle    dw    0        ; temp, a handle not in use
  130. found_handle    dw    0        ; temp, handle for our packet
  131. receive_ptr    dd    0        ; the pkt receive service routine
  132.  
  133.     public    send_head, send_tail
  134. send_head    dd    0        ; head of transmit queue
  135. send_tail    dd    0        ; tail of transmit queue
  136.  
  137. statistics_list    label    dword
  138. packets_in    dw    ?,?
  139. packets_out    dw    ?,?
  140. bytes_in    dw    ?,?
  141. bytes_out    dw    ?,?
  142. errors_in    dw    ?,?
  143. errors_out    dw    ?,?
  144. packets_dropped    dw    ?,?        ;dropped due to no type handler.
  145.  
  146. savess        dw    ?        ;saved during the stack swap.
  147. savesp        dw    ?
  148.  
  149. regs    struc                ; stack offsets of incoming regs
  150. _ES    dw    ?
  151. _DS    dw    ?
  152. _BP    dw    ?
  153. _DI    dw    ?
  154. _SI    dw    ?
  155. _DX    dw    ?
  156. _CX    dw    ?
  157. _BX    dw    ?
  158. _AX    dw    ?
  159. _IP    dw    ?
  160. _CS    dw    ?
  161. _F    dw    ?            ; flags, Carry flag is bit 0
  162. regs    ends
  163.  
  164. CY    equ    0001h
  165. EI    equ    0200h
  166.  
  167.  
  168. bytes    struc                ; stack offsets of incoming regs
  169.     dw    ?            ; es, ds, bp, di, si are 16 bits
  170.     dw    ?
  171.     dw    ?
  172.     dw    ?
  173.     dw    ?
  174. _DL    db    ?
  175. _DH    db    ?
  176. _CL    db    ?
  177. _CH    db    ?
  178. _BL    db    ?
  179. _BH    db    ?
  180. _AL    db    ?
  181. _AH    db    ?
  182. bytes    ends
  183.  
  184.     public    our_isr, their_isr
  185. their_isr    dd    0        ; original owner of pkt driver int
  186.  
  187. our_isr:
  188.     jmp    our_isr_0        ;the required signature.
  189.     db    'PKT DRVR',0
  190. our_isr_0:
  191.     assume    ds:nothing
  192.     push    ax
  193.     push    bx
  194.     push    cx
  195.     push    dx
  196.     push    si
  197.     push    di
  198.     push    bp
  199.     push    ds
  200.     push    es
  201.     cld
  202.     mov    bx,cs            ;set up ds.
  203.     mov    ds,bx
  204.     assume    ds:code
  205.     mov    bp,sp            ;we use bp to access the original regs.
  206.     and    _F[bp],not CY        ;start by clearing the carry flag.
  207.  
  208.     test    flagbyte,CALLED_ETOPEN    ; have we initialized the card?
  209.     jnz    our_isr_cont        ; yes
  210.     push    ax            ; save lots of registers
  211.     push    bx
  212.     push    cx
  213.     push    dx
  214.     push    si
  215.     push    di
  216.     push    bp
  217.     push    ds
  218.     push    es
  219.  
  220.     call    etopen            ; init the card
  221.  
  222.     pop    es            ; restore lots of registers
  223.     pop    ds
  224.     pop    bp
  225.     pop    di
  226.     pop    si
  227.     pop    dx
  228.     pop    cx
  229.     pop    bx
  230.     pop    ax
  231.     mov    dh,CANT_RESET        ; (actually can't initialize)
  232.     jc    our_isr_error
  233.     or    flagbyte,CALLED_ETOPEN    ; remember this fact
  234.  our_isr_cont:
  235.  
  236.     mov    bl,ah            ;jump to the correct function.
  237.     mov    bh,0
  238.     cmp    bx,25            ;only twenty five functions right now.
  239.     mov    dh,BAD_COMMAND        ;in case we find a bad number.
  240.     ja    our_isr_error
  241.     add    bx,bx            ;*2
  242.     call    functions[bx]
  243.     jnc    our_isr_return
  244. our_isr_error:
  245.     mov    _DH[bp],dh
  246.     or    _F[bp],CY        ;return their carry flag.
  247. our_isr_return:
  248.     pop    es
  249.     pop    ds
  250.     pop    bp
  251.     pop    di
  252.     pop    si
  253.     pop    dx
  254.     pop    cx
  255.     pop    bx
  256.     pop    ax
  257.     iret
  258.  
  259.     public    re_enable_interrupts
  260. re_enable_interrupts:
  261. ; Possibly re-enable interrupts.  We put this here so that other routines
  262. ; don't need to know how we put things on the stack.
  263.     test    _F[bp], EI        ; Were interrupts enabled on pkt driver entry?
  264.     je    re_enable_interrupts_1    ; No.
  265.     sti                ; Yes, re-enable interrupts now.
  266. re_enable_interrupts_1:
  267.     ret
  268.  
  269.  
  270. f_not_implemented:
  271.     mov    dh,BAD_COMMAND
  272.     stc
  273.     ret
  274.  
  275.  
  276. f_driver_info:
  277. ;    As of 1.08, the handle is optional, so we no longer verify it.
  278. ;    call    verify_handle
  279.     cmp    _AL[bp],0ffh        ; correct calling convention?
  280.     jne    f_driver_info_1        ; ne = incorrect, fail
  281.  
  282.                     ;For enhanced PD, if they call
  283.     cmp    _BX[bp],offset handles    ;with a handle, give them the
  284.                     ;class they think it is
  285.     jb    default_handle
  286.     cmp    _BX[bp],offset end_handles ;otherwise default to first class
  287.     jae    default_handle
  288.     mov    bx, _BX[bp]
  289.     cmp    [bx].in_use,0        ;if it's not in use, it's bad.
  290.     je    default_handle
  291.     mov    al, [bx].class
  292.     mov    _CH[bp], al
  293.     jmp    short got_handle
  294.  
  295. default_handle:
  296.     mov    al,driver_class
  297.     mov    _CH[bp],al
  298. got_handle:
  299.  
  300.     mov    _BX[bp],majver        ;version
  301.     mov    al,driver_type
  302.     cbw
  303.     mov    _DX[bp],ax
  304.     mov    _CL[bp],0        ;number zero.
  305.     mov    _DS[bp],ds        ; point to our name in their ds:si
  306.     mov    _SI[bp],offset driver_name
  307.     mov    al,driver_function
  308.     mov    _AL[bp],al
  309.     clc
  310.     ret
  311. f_driver_info_1:
  312.     stc
  313.     ret
  314.  
  315.  
  316. f_set_rcv_mode:
  317.     call    verify_handle
  318.  
  319.     cmp    cx,rcv_mode_num        ;are we already using that mode?
  320.     je    f_set_rcv_mode_4    ;yes, no need to check anything.
  321.  
  322.     mov    dx,bx            ;remember our handle.
  323.     mov    bx,offset handles    ; check that all handles are free
  324. f_set_rcv_mode_2:
  325.     cmp    bx,dx            ; is this our handle?
  326.     je    f_set_rcv_mode_3    ; yes, of course it's not free.
  327.     cmp    [bx].in_use,0        ; is this handle free?
  328.     jne    f_set_rcv_mode_1    ; ne = no, can't change
  329. f_set_rcv_mode_3:
  330.     add    bx,(size per_handle)    ; next handle
  331.     cmp    bx,offset end_handles    ; examined all handles?
  332.     jb    f_set_rcv_mode_2    ; b = no, continue examination
  333.  
  334.     mov    cx,_CX[bp]        ;get the desired receive mode.
  335.     cmp    cx,rcv_modes        ;do they have this many modes?
  336.     jae    f_set_rcv_mode_1    ;no - must be a bad mode for us.
  337.     mov    bx,cx
  338.     add    bx,bx            ;we're accessing words, not bytes.
  339.     mov    ax,rcv_modes[bx]+2    ;get the handler for this mode.
  340.     or    ax,ax            ;do they have one?
  341.     je    f_set_rcv_mode_1    ;no - must be a bad mode for us.
  342.     mov    rcv_mode_num,cx        ;yes - remember the number and
  343.     call    ax            ;  call it.
  344. f_set_rcv_mode_4:
  345.     clc
  346.     ret
  347. f_set_rcv_mode_1:
  348.     mov    dh,BAD_MODE
  349.     stc
  350.     ret
  351.  
  352.  
  353. f_get_rcv_mode:
  354.     call    verify_handle
  355.     mov    ax,rcv_mode_num        ;return the current receive mode.
  356.     mov    _AX[bp],ax
  357.     clc
  358.     ret
  359.  
  360.  
  361. f_set_multicast_list:
  362.     mov    cx,_CX[bp]        ;Tell them how much room they have.
  363.  
  364. ;verify that they supplied an even number of EADDR's.
  365.     mov    ax,cx
  366.     xor    dx,dx
  367.     mov    bx,EADDR_LEN
  368.     div    bx
  369.     or    dx,dx            ;zero remainder?
  370.     jne    f_set_multicast_list_2    ;no, we don't have an even number of
  371.                     ;  addresses.
  372.  
  373.     cmp    ax,MAX_MULTICAST    ;is this too many?
  374.     ja    f_set_multicast_list_3    ;yes - return NO_SPACE
  375. f_set_multicast_list_1:
  376.     mov    multicast_count,ax    ;remember the number of addresses.
  377.     push    cs
  378.     pop    es
  379.     mov    di,offset multicast_addrs
  380.     push    ds
  381.     mov    ds,_ES[bp]        ; get ds:si -> new list.
  382.     mov    si,_DI[bp]
  383.     push    cx
  384.     rep    movsb
  385.     pop    cx
  386.     pop    ds
  387.  
  388.     mov    si,offset multicast_addrs
  389.     call    set_multicast_list
  390.     ret
  391. f_set_multicast_list_2:
  392.     mov    dh,BAD_ADDRESS
  393.     stc
  394.     ret
  395. f_set_multicast_list_3:
  396.     mov    dh,NO_SPACE
  397.     stc
  398.     ret
  399.  
  400.  
  401. f_get_multicast_list:
  402.     mov    _ES[bp],ds        ;return what we have remembered.
  403.     mov    _DI[bp],offset multicast_addrs
  404.     mov    ax,EADDR_LEN        ;multiply the count by the length.
  405.     mul    multicast_count
  406.     mov    _CX[bp],ax        ;because they want total bytes.
  407.     clc
  408.     ret
  409.  
  410.  
  411. f_get_statistics:
  412.     call    verify_handle        ;just in case.
  413.     mov    _DS[bp],ds
  414.     mov    _SI[bp],offset statistics_list
  415.     clc
  416.     ret
  417.  
  418.  
  419. access_type_class:
  420.     mov    dh,NO_CLASS
  421.     stc
  422.     ret
  423.  
  424. access_type_type:
  425.     mov    dh,NO_TYPE
  426.     stc
  427.     ret
  428.  
  429. access_type_number:
  430.     mov    dh,NO_NUMBER
  431.     stc
  432.     ret
  433.  
  434. access_type_bad:
  435.     mov    dh,BAD_TYPE
  436.     stc
  437.     ret
  438.  
  439. ;register caller of pkt TYPE
  440. f_access_type:
  441.     mov    bx, offset driver_class
  442. access_type_9:
  443.     mov    al, [bx]        ;get the next class.
  444.     inc    bx
  445.     or    al,al            ;end of the list?
  446.     je    access_type_class    ;class failed (story of my life)
  447.     cmp    _AL[bp],al        ;our class?
  448.     jne    access_type_9        ;no, try again
  449. access_type_1:
  450.     cmp    _BX[bp],-1        ;generic type?
  451.     je    access_type_2        ;yes.
  452.     mov    al,driver_type
  453.     cbw
  454.     cmp    _BX[bp],ax        ;our type?
  455.     jne    access_type_type    ;no.
  456. access_type_2:
  457.     cmp    _DL[bp],0        ;generic number?
  458.     je    access_type_3
  459.     cmp    _DL[bp],1        ;our number?
  460.     jne    access_type_number
  461. access_type_3:
  462.     cmp    _CX[bp],MAX_P_LEN    ;is the type length too long?
  463.     ja    access_type_bad        ;yes - can't be ours.
  464.  
  465. ; now we do two things--look for an open handle, and check the existing
  466. ; handles to see if they're replicating a packet type.
  467.  
  468.     mov    free_handle,0        ;remember no free handle yet.
  469.     mov    bx,offset handles
  470. access_type_4:
  471.     cmp    [bx].in_use,0        ;is this handle in use?
  472.     je    access_type_5        ;no - don't check the type.
  473.     mov    al, _AL[bp]        ;is this handle the same class as
  474.     cmp    al, [bx].class        ;  they're want?
  475.     jne    short access_type_6
  476.     mov    es,_DS[bp]        ;get a pointer to their type
  477.     mov    di,_SI[bp]        ;  from their ds:si to our es:di
  478.     mov    cx,_CX[bp]        ;get the minimum of their length
  479.                     ;  and our length.  As currently
  480.                     ;  implemented, only one receiver
  481.                     ;  gets the packets, so we have to
  482.                     ;  ensure that the shortest prefix
  483.                     ;  is unique.
  484.     cmp    cx,[bx].packet_type_len    ;Are we less specific than they are?
  485.     jb    access_type_8        ;no.
  486.     mov    cx,[bx].packet_type_len    ;yes - use their count.
  487. access_type_8:
  488.     lea    si,[bx].packet_type
  489.     or    cx,cx            ; pass-all TYPE? (zero TYPE length)
  490.     jne    access_type_7        ; ne = no
  491.     mov    bx,offset handles+(MAX_HANDLE-1)*(size per_handle)
  492.     jmp    short access_type_5    ; put pass-all last
  493. access_type_7:
  494.     repe    cmpsb
  495.     jne    short access_type_6    ;go look at the next one.
  496. access_type_inuse:
  497.     mov    dh,TYPE_INUSE        ;a handle has been assigned for TYPE
  498.     stc                ;and we can't assign another
  499.     ret
  500. access_type_5:                ;handle is not in use
  501.     cmp    free_handle,0        ;found a free handle yet?
  502.     jne    access_type_6        ;yes.
  503.     mov    free_handle,bx        ;remember a free handle
  504. access_type_6:
  505.     add    bx,(size per_handle)    ;go to the next handle.
  506.     cmp    bx,offset end_handles    ;examined all handles?
  507.     jb    access_type_4        ;no, continue.
  508.  
  509.     mov    bx,free_handle        ;did we find a free handle?
  510.     or    bx,bx
  511.     je    access_type_space    ;no - return error.
  512.  
  513.     mov    [bx].in_use,1        ;remember that we're using it.
  514.  
  515.     mov    ax,_DI[bp]        ;remember the receiver type.
  516.     mov    [bx].receiver.offs,ax
  517.     mov    ax,_ES[bp]
  518.     mov    [bx].receiver.segm,ax
  519.  
  520.     push    ds
  521.     mov    ax,ds
  522.     mov    es,ax
  523.     mov    ds,_DS[bp]        ;remember their type.
  524.     mov    si,_SI[bp]
  525.     mov    cx,_CX[bp]
  526.     mov    es:[bx].packet_type_len,cx    ; remember the TYPE length
  527.     lea    di,[bx].packet_type
  528.     rep    movsb
  529.  
  530.     lds    si,es:[bx].receiver    ;copy the first 8 bytes
  531.     lea    di,[bx].receiver_sig    ; to the receiver signature.
  532.     mov    cx,8/2
  533.     rep    movsw
  534.  
  535.     pop    ds
  536.  
  537.     mov    al, _AL[bp]
  538.     mov    [bx].class, al
  539.  
  540.     mov    _AX[bp],bx        ;return the handle to them.
  541.  
  542.     clc
  543.     ret
  544.  
  545.  
  546. access_type_space:
  547.     mov    dh,NO_SPACE
  548.     stc
  549.     ret
  550.  
  551. f_release_type:
  552.     call    verify_handle        ;mark this handle as being unused.
  553.     mov    [bx].in_use,0
  554.     clc
  555.     ret
  556.  
  557.  
  558. f_send_pkt:
  559. ;ds:si -> buffer, cx = length
  560. ; XXX Should re-enable interrupts here, but some drivers are broken.
  561. ; Possibly re-enable interrupts.
  562. ;    test _F[bp], EI        ; Were interrupts enabled on pkt driver entry?
  563. ;    je    f_send_pkt_1    ; No.
  564. ;    sti            ; Yes, re-enable interrupts now.
  565. ;f_send_pkt_1:
  566.  
  567. ;following two instructions not needed because si and cx haven't been changed.
  568. ;    mov    si,_SI[bp]
  569. ;    mov    cx,_CX[bp]    ; count of bytes in the packet.
  570.     linc    packets_out
  571.     add    bytes_out.offs,cx    ;add up the received bytes.
  572.     adc    bytes_out.segm,0
  573.  
  574.     push    ds        ; set up proper ds for the buffer
  575.     mov    ds,_DS[bp]    ; address of buffer from caller's ds.
  576.     assume    ds:nothing, es:nothing
  577.  
  578. ; If -n option take Ethernet encapsulated Novell IPX packets (from BYU's 
  579. ; PDSHELL) and change them to be IEEE 802.3 encapsulated.
  580. EPROT_OFF    equ    EADDR_LEN*2
  581.     test    cs:flagbyte,N_OPTION
  582.     jz    f_send_pkt_2
  583.     cmp    ds:[si].EPROT_OFF,3781h ; if not Novell (prot 8137)
  584.     jne    f_send_pkt_2        ;  don't tread on it
  585.     push    ax            ; get scratch reg
  586.     mov    ax,[si].EPROT_OFF+4    ; get len
  587.     xchg    ah,al
  588.     inc    ax            ; make even (rounding up)
  589.     and    al,0feh
  590.     xchg    ah,al
  591.     mov    ds:[si].EPROT_OFF,ax    ; save in prot field
  592.     pop    ax            ; restore old contents
  593. f_send_pkt_2:
  594.     call    send_pkt
  595.     pop    ds
  596.     assume    ds:code
  597.     ret
  598.  
  599.  
  600. f_as_send_pkt:
  601. ;es:di -> iocb.
  602.     test    driver_function,4    ; is this a high-performance driver?
  603.     je    f_as_send_pkt_2        ; no.
  604. ; Possibly re-enable interrupts.
  605.     test _F[bp], EI            ; Were interrupts enabled on pkt driver entry?
  606.     je    f_as_send_pkt_1        ; No.
  607.     sti                ; Yes, re-enable interrupts now.
  608. f_as_send_pkt_1:
  609.     push    ds            ; set up proper ds for the buffer
  610.     lds    si,es:[di].buffer    ; ds:si -> buffer
  611.     assume    ds:nothing
  612.     mov    cx,es:[di].len        ; cx = length
  613.     linc    packets_out
  614.     add    bytes_out.offs,cx    ; add up the received bytes.
  615.     adc    bytes_out.segm,0
  616.  
  617. ;ds:si -> buffer, cx = length, es:di -> iocb.
  618.     call    as_send_pkt
  619.     pop    ds
  620.     assume    ds:code
  621.     ret
  622. f_as_send_pkt_2:
  623.     mov dh,    BAD_COMMAND        ; return an error.
  624.     stc
  625.     ret
  626.  
  627.  
  628. f_drop_pkt:
  629. ; es:di -> iocb.
  630.     test    driver_function,4    ; is this a high-performance driver?
  631.     je    f_as_send_pkt_1        ; no.
  632.     push    ds            ; Preserve ds
  633.     mov    si,offset send_head    ; Get head offset
  634. dp_loop:
  635.     mov    ax,ds:[si]        ; Get offset
  636.     mov    dx,ds:[si+2]        ; Get segment
  637.     mov    bx,ax
  638.     or    bx,dx            ; End of list?
  639.     je    dp_endlist        ; Yes
  640.     cmp    ax,di            ; Offsets equal?
  641.     jne    dp_getnext        ; No
  642.     mov    bx,es
  643.     cmp    dx,bx            ; Segments equal?
  644.     jne    dp_getnext        ; No
  645.     call    drop_pkt        ; Pass to driver
  646.     les    di,es:[di].next        ; Get next segment:offset
  647.     mov    ds:[si],di        ; Set next offset
  648.     mov    ds:[si+2],es        ; Set next segment
  649.     pop    ds            ; Restore ds
  650.     clc
  651.     ret
  652. dp_getnext:
  653.     mov    ds,dx            ; Get next segment
  654.     mov    si,ax            ; Get next iocb offset
  655.     lea    si,ds:[si].next        ; Get next iocb next ptr offset
  656.     jmp    dp_loop            ; Try again
  657. dp_endlist:
  658.     pop    ds            ; Restore ds
  659.     mov    dh,BAD_IOCB        ; Return error
  660.     stc                ; Set carry
  661.     ret
  662.  
  663.  
  664. f_terminate:
  665.     call    verify_handle        ; must have a handle
  666.  
  667. f_terminate_1:
  668.     mov    [bx].in_use,0        ; mark handle as free
  669.     mov    bx,offset handles    ; check that all handles are free
  670. f_terminate_2:
  671.     cmp    [bx].in_use,0        ; is this handle free?
  672.     jne    f_terminate_4        ; ne = no, so can't exit completely
  673.     add    bx,(size per_handle)    ; next handle
  674.     cmp    bx,offset end_handles    ; examined all handles?
  675.     jb    f_terminate_2        ; b = no, continue examination
  676.  
  677. ;
  678. ; Now disable interrupts
  679. ;
  680.     mov    al,int_no
  681.     or    al,al            ;are they using a hardware interrupt?
  682.     je    f_terminate_no_irq    ;no.
  683.     call    maskint
  684.  
  685. ;
  686. ; Now return the interrupt to their handler.
  687. ;
  688.     mov    ah,25h            ;get the old interrupt into es:bx
  689.     mov    al,int_no
  690.     add    al,8
  691.     cmp    al,8+8            ;is it a slave 8259 interrupt?
  692.     jb    f_terminate_3        ;no.
  693.     add    al,70h - (8+8)        ;map it to the real interrupt.
  694. f_terminate_3:
  695.     push    ds
  696.     lds    dx,their_recv_isr
  697.     int    21h
  698.     pop    ds
  699.  
  700. f_terminate_no_irq:
  701.     call    terminate        ;terminate the hardware.
  702.  
  703.     mov    al,packet_int_no    ;release our_isr.
  704.     mov    ah,25h
  705.     push    ds
  706.     lds    dx,their_isr
  707.     int    21h
  708.     pop    ds
  709.  
  710. ;
  711. ; Now free our memory
  712. ;
  713.     push    cs
  714.     pop    es
  715.     mov    ah,49h
  716.     int    21h
  717.     clc
  718.     ret
  719. f_terminate_4:
  720.     mov    dh, CANT_TERMINATE
  721.     stc
  722.     ret
  723.  
  724.  
  725.  
  726. f_get_address:
  727. ;    call    verify_handle
  728. ;    mov    es,_ES[bp]        ; get new one
  729. ;    mov    di,_DI[bp]        ; get pointer, es:di is ready
  730.     mov    cx,_CX[bp]        ;Tell them how much room they have.
  731.     cmp    have_my_address,0    ;has our address been set?
  732.     jne    get_address_set        ;yes - go report it.
  733.     call    get_address        ;no, can we get the address?
  734.     jc    get_address_space    ;no - we must not have enough space.
  735.     mov    _CX[bp],cx        ;Tell them how long our address is.
  736.     clc
  737.     ret
  738. get_address_set:
  739.     cmp    cx,my_address_len    ;is there enough room?
  740.     jb    get_address_space    ;no.
  741.     mov    cx,my_address_len    ;yes - get our address length.
  742.     mov    _CX[bp],cx        ;Tell them how long our address is.
  743.     mov    si,offset my_address    ;copy it into their area.
  744.     rep    movsb
  745.     clc
  746.     ret
  747.  
  748. get_address_space:
  749.     mov    dh,NO_SPACE
  750.     stc
  751.     ret
  752.  
  753.  
  754. f_set_address:
  755.     mov    bx,offset handles
  756.     mov    cl,0            ;number of handles in use.
  757. f_set_address_1:
  758.     add    cl,[bx].in_use        ;is this handle in use?
  759.     add    bx,(size per_handle)    ;go to the next handle.
  760.     cmp    bx,offset end_handles
  761.     jb    f_set_address_1
  762.  
  763.     cmp    cl,1            ;more than one handle in use?
  764.     ja    f_set_address_inuse    ;yes - we can't set the address
  765.  
  766.     mov    ds,_ES[bp]        ; set new one
  767.     assume    ds:nothing
  768.     mov    si,_DI[bp]        ; set pointer, ds:si is ready
  769.     mov    cx,_CX[bp]        ;Tell them how much address is being set.
  770.     call    set_address
  771. ;set_address restores ds.
  772.     jc    f_set_address_exit    ;Did it work?
  773.     mov    _CX[bp],cx        ;yes - return our address length.
  774.  
  775.     cmp    cx,MAX_ADDR_LEN        ;is it too long for us to remember?
  776.     ja    f_set_address_too_long    ;yes, return a too-long error.
  777.  
  778.     mov    ds,_ES[bp]        ; set new one
  779.     mov    si,_DI[bp]        ; set pointer, ds:si is ready
  780.     mov    ax,cs
  781.     mov    es,ax
  782.     mov    my_address_len,cx    ;remember how long our address is.
  783.     mov    di,offset my_address
  784.     rep    movsb
  785.     mov    have_my_address,1
  786.     mov    ds,ax            ;restoer ds.
  787.     assume    ds:code
  788.     clc
  789.     ret
  790. f_set_address_inuse:
  791.     mov    dh,CANT_SET
  792.     stc
  793.     ret
  794. f_set_address_too_long:
  795.     mov    dh,NO_SPACE
  796.     stc
  797. f_set_address_exit:
  798.     ret
  799.  
  800.  
  801. f_reset_interface:
  802.     call    verify_handle
  803.     call    reset_interface
  804.     clc
  805.     ret
  806.  
  807.  
  808. ; Stop the packet driver doing upcalls. Also a following terminate will
  809. ; always succed (no in use handles any longer).
  810. f_stop:
  811.     mov    bx,offset handles
  812. f_stop_2:
  813.     mov    [bx].in_use,0
  814.     add    bx,(size per_handle)    ; next handle
  815.     cmp    bx,offset end_handles
  816.     jb    f_stop_2
  817.     clc
  818.     ret
  819.  
  820.  
  821. f_get_parameters:
  822. ;strictly speaking, this function only works for high-performance drivers.
  823.     test    driver_function,4    ;is this a high-performance driver?
  824.     jne    f_get_parameters_1    ;yes.
  825.     mov    dh,BAD_COMMAND        ;no - return an error.
  826.     stc
  827.     ret
  828. f_get_parameters_1:
  829.     mov    _ES[bp],cs
  830.     mov    _DI[bp],offset parameter_list
  831.     clc
  832.     ret
  833.  
  834.  
  835. verify_handle:
  836. ;Ensure that their handle is real.  If it isn't, we pop off our return
  837. ;address, and return to *their* return address with cy set.
  838.     mov    bx,_BX[bp]        ;get the handle they gave us
  839.     cmp    bx,offset handles
  840.     jb    verify_handle_bad    ;no - must be bad.
  841.     cmp    bx,offset end_handles
  842.     jae    verify_handle_bad    ;no - must be bad.
  843.     cmp    [bx].in_use,0        ;if it's not in use, it's bad.
  844.     je    verify_handle_bad
  845.     ret
  846. verify_handle_bad:
  847.     mov    dh,BAD_HANDLE
  848.     add    sp,2            ;pop off our return address.
  849.     stc
  850.     ret
  851.  
  852.  
  853.     public    set_recv_isr
  854. set_recv_isr:
  855.     mov    ah,35h            ;get the old interrupt into es:bx
  856.     mov    al,int_no        ; board's interrupt vector
  857.     or    al,al
  858.     je    set_isr_no_irq
  859.     add    al,8
  860.     cmp    al,8+8            ;is it a slave 8259 interrupt?
  861.     jb    set_recv_isr_1        ;no.
  862.     add    al,70h - 8 - 8        ;map it to the real interrupt.
  863. set_recv_isr_1:
  864.     int    21h
  865.     mov    their_recv_isr.offs,bx    ;remember the old seg:off.
  866.     mov    their_recv_isr.segm,es
  867.  
  868.     mov    ah,25h            ;now set our recv interrupt.
  869.     mov    dx,offset recv_isr
  870.     int    21h
  871.  
  872.     mov    al,int_no        ; Now enable interrupts
  873.     call    unmaskint
  874.  
  875. set_isr_no_irq:
  876.     ret
  877.  
  878.     public    count_in_err
  879. count_in_err:
  880.     assume    ds:nothing
  881.     linc    errors_in
  882.     ret
  883.  
  884.     public    count_out_err
  885. count_out_err:
  886.     assume    ds:nothing
  887.     linc    errors_out
  888.     ret
  889.  
  890. their_recv_isr    dd    0        ; original owner of board int
  891.  
  892. ;
  893. ; I have had a problem with some hardware which under extreme LAN loading
  894. ; conditions will re-enter the recv_isr. Since the 8259 interrupts for
  895. ; the card are masked off, and the card's interrupt mask register is 
  896. ; cleared (in 8390.asm at least) disabling the card from interrupting, this
  897. ; is clearly a hardware problem. Due to the low frequencey of occurance, and
  898. ; extreme conditions under which this happens, it is not lilely to be fixed
  899. ; in hardware any time soon, plus retrofitting of hardware in the field will
  900. ; not happen. To protect the driver from the adverse effects of this I am
  901. ; adding a simple re-entrancy trap here.  - gft - 910617
  902. ;
  903.  
  904. in_recv_isr    db     0    ; flag to trap re-entrancy
  905.  
  906. recv_isr:
  907.     cmp    in_recv_isr, 0
  908.     je    no_re_enter
  909.     iret
  910.  
  911. no_re_enter:
  912.     mov    in_recv_isr, 1
  913.  
  914. ; I realize this re-entrancy trap is not perfect, you could be re-entered
  915. ; anytime before the above instruction. However since the stacks have not
  916. ; been swapped re-entrancy here will only appear to be a spurious interrupt.
  917. ; - gft - 910617
  918.  
  919. ; In order to achieve back-to-back packet transmissions, we handle the
  920. ; latency-critical portion of transmit interrupts first.  The xmit
  921. ; interrupt routine should only start the next transmission, but do
  922. ; no other work.  It may only touch ax and dx (the only register necessary
  923. ; for doing "out" instructions) unless it first pushes any other registers
  924. ; itself.
  925.     push    ax
  926.     push    dx
  927.     call    xmit
  928.  
  929. ; Now switch stacks, push remaining registers, and do remaining interrupt work.
  930.     push    ds
  931.     mov    ax,cs            ;ds = cs.
  932.     mov    ds,ax
  933.     assume    ds:code
  934.  
  935.     mov    savesp,sp
  936.     mov    savess,ss
  937.  
  938.     mov    ss,ax
  939.     mov    sp,offset our_stack
  940.     cld
  941.  
  942.     push    bx
  943.     push    cx
  944.     push    si
  945.     push    di
  946.     push    bp
  947.     push    es
  948.  
  949. ; The following comment is wrong in that we now do a specific EOI command,
  950. ; and because we don't enable interrupts (even though we should).
  951.  
  952. ; Chips & Technologies 8259 clone chip seems to be very broken.  If you
  953. ; send it a Non Specific EOI command, it clears all In Service Register
  954. ; bits instead of just the one with the highest priority (as the Intel
  955. ; chip does and clones should do).  This bug causes our interrupt
  956. ; routine to be reentered if: 1. we reenable processor interrupts;
  957. ; 2. we reenable device interrupts; 3. a timer or other higher priority
  958. ; device interrupt now comes in; 4. the new interrupting device uses
  959. ; a Non Specific EOI; 5. our device interrupts again.  Because of
  960. ; this bug, we now completely mask our interrupts around the call
  961. ; to "recv", the real device interrupt handler.  This allows us
  962. ; to send an EOI instruction to the 8259 early, before we actually
  963. ; reenable device interrupts.  Since the interrupt is masked, we
  964. ; are still guaranteed not to get another interrupt from our device
  965. ; until the interrupt handler returns.  This has another benefit:
  966. ; we now no longer prevent other devices from interrupting while our
  967. ; interrupt handler is running.  This is especially useful if we have
  968. ; other (multiple) packet drivers trying to do low-latency transmits.
  969.     mov    al,int_no    ; Disable further device interrupts
  970.     call    maskint
  971.  
  972. ; The following is from Bill Rust, <wjr@ftp.com>
  973. ; this code dismisses the interrupt at the 8259. if the interrupt number
  974. ;  is > 8 then it requires fondling two PICs instead of just one.
  975.     mov    al, int_no    ; get hardware int #
  976.     cmp    al, 8        ; see if its on secondary PIC
  977.     jg    recv_isr_4
  978.     add    al, 60h        ; make specific EOI dismissal
  979.     out    20h, al
  980.     jmp    recv_isr_3    ; all done
  981. recv_isr_4:
  982.     add    al,60h - 8    ; make specific EOI (# between 9 & 15).
  983.     out    0a0h,al        ; Secondary 8259 (PC/AT only)
  984.     mov    al,62h        ; Acknowledge on primary 8259.
  985.     out    20h,al
  986. recv_isr_3:
  987.  
  988. ;    sti                ; Interrupts are now completely safe
  989.     call    recv
  990.  
  991.     cli                ;interrupts *must* be off between
  992.                     ;here and the stack restore, because
  993.                     ;if we have one of our interrupts
  994.                     ;pending, we would trash our stack.
  995. ;
  996. ; According to Novell IMSP Technical Memo #2, Lost Interrupts and NetWare,
  997. ; some LAN controllers can glitch the interrupt line when the interrupt
  998. ; mask register on the LAN controller is enabled to allow the LAN controller
  999. ; to interrupt. This can cause spurious/lost interrupt problems, especially
  1000. ; on some 486 processors which latch the int line to the processor. To prevent
  1001. ; glitches from being propogated throug, move the call to recv_exiting (re-
  1002. ; enables interrupts on the LAN card) to before when the 8259 interrupts are
  1003. ; unmasked. - gft - 910617
  1004.  
  1005.     call    recv_exiting
  1006.  
  1007.     mov    al,int_no    ; Now reenable device interrupts
  1008.     call    unmaskint
  1009.  
  1010.     pop    es
  1011.     pop    bp
  1012.     pop    di
  1013.     pop    si
  1014.     pop    cx
  1015.     pop    bx
  1016.  
  1017.     mov    ss,savess
  1018.     mov    sp,savesp
  1019.  
  1020. ; move the next call up to above the call unmaskint - gft - 910617
  1021. ;    call    recv_exiting        ;this routine can enable interrupts.
  1022. ; DDP - This is a BIG mistake.  This routine SHOULD NOT enable interrupts.
  1023. ;    doing so can cause interrupt recursion and blow your stack.
  1024. ;    Processor interrupts SHOULD NOT be enabled after enabling device
  1025. ;    interrupts until after the "iret".  You will lose atleast 12 bytes
  1026. ;    on the stack for each recursion.
  1027.  
  1028.     pop    ds
  1029.     assume    ds:nothing
  1030.     pop    dx
  1031.     pop    ax
  1032.     mov    in_recv_isr, 0    ; clear the re-entrancy flag - gft - 901617
  1033.     iret
  1034.  
  1035.  
  1036.     public    maskint
  1037. maskint:
  1038.     or    al,al            ;are they using a hardware interrupt?
  1039.     je    maskint_1        ;no, don't mask off the timer!
  1040.  
  1041.     assume    ds:code
  1042.     mov    dx,21h            ;assume the master 8259.
  1043.     cmp    al,8            ;using the slave 8259 on an AT?
  1044.     jb    mask_not_irq2
  1045.     mov    dx,0a1h            ;go disable it on slave 8259
  1046.     sub    al,8
  1047. mask_not_irq2:
  1048.     mov    cl,al
  1049.  
  1050.     in    al,dx            ;disable them on the correct 8259.
  1051.     mov    ah,1            ;set the bit.
  1052.     shl    ah,cl
  1053.     or    al,ah
  1054. ;
  1055. ; 500ns Stall required here, per INTEL documentation for eisa machines
  1056. ; - gft - 910617
  1057. ;
  1058.     push    ax
  1059.     in    al,61h    ; 1.5 - 3 uS should be plenty
  1060.     in    al,61h
  1061.     in    al,61h
  1062.     pop    ax
  1063.     out    dx,al
  1064. maskint_1:
  1065.     ret
  1066.  
  1067.  
  1068.     public    unmaskint
  1069. unmaskint:
  1070.     assume    ds:code
  1071.     mov    dx,21h            ;assume the master 8259.
  1072.     mov    cl,al
  1073.     cmp    cl,8            ;using the slave 8259 on an AT?
  1074.     jb    unmask_not_irq2        ;no
  1075.     in    al,dx            ;get master mask
  1076.     and    al,not (1 shl 2)    ; and clear slave cascade bit in mask
  1077.     out    dx,al            ;set new master mask (enable slave int)
  1078. ;
  1079. ; 500ns Stall required here, per INTEL documentation for eisa machines
  1080. ; - gft - 910617
  1081. ;
  1082.     push    ax
  1083.     in    al,61h    ; 1.5 - 3 uS should be plenty
  1084.     in    al,61h
  1085.     in    al,61h
  1086.     pop    ax
  1087.     mov    dx,0a1h            ;go enable int on slave 8259
  1088.     sub    cl,8
  1089. unmask_not_irq2:
  1090.  
  1091.     in    al,dx            ;enable interrupts on the correct 8259.
  1092.     mov    ah,1            ;clear the bit.
  1093.     shl    ah,cl
  1094.     not    ah
  1095.     and    al,ah
  1096. ;
  1097. ; 500ns Stall required here, per INTEL documentation for eisa machines
  1098. ; - gft - 910617
  1099. ;
  1100.     push    ax
  1101.     in    al,61h    ; 1.5 - 3 uS should be plenty
  1102.     in    al,61h
  1103.     in    al,61h
  1104.     pop    ax
  1105.     out    dx,al
  1106.  
  1107.     ret
  1108.  
  1109.  
  1110.     public    recv_find
  1111. recv_find:
  1112. ;called when we want to determine what to do with a received packet.
  1113. ;enter with cx = packet length, es:di -> packet type, dl = packet class.
  1114. ;exit with es:di = 0 if the packet is not desired, or es:di -> packet buffer
  1115. ;  to be filled by the driver.
  1116.     assume    ds:code, es:nothing
  1117.     push    cx
  1118.  
  1119. ; If -n option take IEEE 802.3 encapsulated packets that could be Novell IPX 
  1120. ; and make them Ethernet encapsulated Novell IPX packets (for PDSHELL).
  1121.     test    flagbyte,N_OPTION
  1122.     jz    not_n_op
  1123.  
  1124. ; Make IEEE 802.3-like packets that could be Novell IPX into BlueBook class
  1125. ; Novell type 8137 packets.
  1126.     cmp    dl,IEEE8023        ;Is this an IEEE 802.3 packet?
  1127.     jne    recv_not_802_3        ;no
  1128.     cmp    word ptr es:[di],0ffffh    ;if this word not ffff
  1129.     jne    recv_not_8137        ;  then not Novell
  1130.     sub    di,2            ; back it up to the 8137 word.
  1131.     mov    es:[di],3781h        ; fake as Novell protocol (8137)
  1132.     mov    dl,BLUEBOOK
  1133.     jmp    short recv_not_8137
  1134. recv_not_802_3:
  1135. ; Convert incoming Ethernet type 8137 IPX packets to type 8138, as with -n in 
  1136. ; effect we can't send type 8137, and it will only confuse Netware.
  1137.     cmp    dl,BLUEBOOK        ;Is this a BLUEBOOK packet?
  1138.     jne    recv_not_8137        ;no, don't change it.
  1139.     cmp    word ptr es:[di],3781h    ;Is it an 8137 packet?
  1140.     jne    recv_not_8137        ;no, don't change it.
  1141.     mov    es:[di],word ptr 3881h    ;yes, mung it slightly.
  1142. recv_not_8137:
  1143. not_n_op:
  1144.  
  1145.     mov    bx,offset handles
  1146. recv_find_1:
  1147.     cmp    [bx].in_use,0        ;is this handle in use?
  1148.     je    recv_find_2        ;no - don't check the type.
  1149.  
  1150.     mov    ax,[bx].receiver.offs    ;do they have a receiver?
  1151.     or    ax,[bx].receiver.segm
  1152.     je    recv_find_2        ;no - they're not serious about it.
  1153.  
  1154.     mov    cx,[bx].packet_type_len    ;compare the packets.
  1155.     lea    si,[bx].packet_type
  1156.     jcxz    recv_find_3        ;if cx is zero, they want them all.
  1157.  
  1158.     cmp    [bx].class, dl        ;is this the right class?
  1159.     jne    recv_find_2        ;no- don't bother
  1160.  
  1161.     push    di
  1162.     repe    cmpsb
  1163.     pop    di
  1164.     je    recv_find_3        ;we've got it!
  1165. recv_find_2:
  1166.     add    bx,(size per_handle)    ;go to the next handle.
  1167.     cmp    bx,offset end_handles
  1168.     jb    recv_find_1
  1169.  
  1170.     linc    packets_dropped
  1171.  
  1172.     pop    cx            ;we didn't find it -- discard it.
  1173. recv_find_5:
  1174.     xor    di,di            ;"return" a null pointer.
  1175.     mov    es,di
  1176.     ret
  1177. recv_find_3:
  1178.     pop    cx            ; the packet_length
  1179.  
  1180.     linc    packets_in
  1181.     add    bytes_in.offs,cx    ;add up the received bytes.
  1182.     adc    bytes_in.segm,0
  1183.  
  1184.     les    di,[bx].receiver    ;remember the receiver upcall.
  1185.     mov    receive_ptr.offs,di
  1186.     mov    receive_ptr.segm,es
  1187.  
  1188.     test    flagbyte,W_OPTION    ;did they select the Windows option?
  1189.     je    recv_find_6        ;no, don't check for the upcall.
  1190.  
  1191. ; does the receiver signature match whats currently in memory?  if not,
  1192. ; jump to fake return
  1193.     push    si
  1194.     push    cx
  1195.     lea    si,[bx].receiver_sig
  1196.     mov    cx,8/2
  1197.     repe    cmpsw
  1198.     pop    cx
  1199.     pop    si
  1200.     jne    recv_find_5
  1201. recv_find_6:
  1202.  
  1203.     mov    found_handle,bx        ;remember what our handle was.
  1204.     mov    ax,0            ;allocate request.
  1205.     stc                ;with stc, flags must be an odd number
  1206.     push    ax            ; save a number that cant be flags
  1207.     pushf                ;save flags in case iret used.
  1208.     call    receive_ptr        ;ask the client for a buffer.
  1209.     ; on return, flags should be at top of stack. if an IRET has been used,
  1210.     ; then 0 will be at the top of the stack
  1211.     pop    bx
  1212.     cmp    bx,0
  1213.     je    recv_find_4        ;0 is at top of stack
  1214.     add    sp,2
  1215. recv_find_4:
  1216.     ret
  1217.  
  1218.  
  1219.     public    recv_copy
  1220. recv_copy:
  1221. ;called after we have copied the packet into the buffer.
  1222. ;enter with ds:si ->the packet, cx = length of the packet.
  1223. ;preserve bx.
  1224.     assume    ds:nothing, es:nothing
  1225.  
  1226.     push    bx
  1227.     mov    bx,found_handle
  1228.     mov    ax,1            ;store request.
  1229.     clc                ;with clc, flags must be an even number
  1230.     push    ax            ; save a number that can't be flags
  1231.     pushf                ;save flags incase iret used.
  1232.     call    receive_ptr        ;ask the client for a buffer.
  1233.     pop    bx
  1234.     cmp    bx,1            ;if this is a 1, IRET was used.
  1235.     je    recv_copy_1
  1236.     pop    bx
  1237. recv_copy_1:
  1238.     pop    bx
  1239.     ret
  1240.  
  1241.     public    send_queue
  1242. send_queue:
  1243. ; Queue an iocb.
  1244. ; Enter with es:di -> iocb, interrupts disabled.
  1245. ; Destroys ds:si.
  1246.     assume    ds:nothing, es:nothing
  1247.     mov    es:[di].next.offs,0    ; Zero next offset
  1248.     mov    es:[di].next.segm,0    ; Zero next segment
  1249.     mov    si,send_head.offs    ; Queue empty?
  1250.     or    si,send_head.segm
  1251.     jnz    sq_notempty        ; No
  1252.     mov    send_head.offs,di    ; Set head offset
  1253.     mov    send_head.segm,es    ; Set head segment
  1254.     jmp    sq_settail
  1255. sq_notempty:                ; Queue is not empty
  1256.     lds    si,send_tail        ; Get tail segment:offset
  1257.     mov    ds:[si].next.offs,di    ; Set next offset
  1258.     mov    ds:[si].next.segm,es    ; Set next segment
  1259. sq_settail:
  1260.     mov    send_tail.offs,di    ; Set tail offset
  1261.     mov    send_tail.segm,es    ; Set tail segment
  1262.     ret
  1263.  
  1264.  
  1265.     public    send_dequeue
  1266. send_dequeue:
  1267. ; Dequeue an iocb and possibly call its upcall.
  1268. ; Enter with device or processor interrupts disabled, ah = return code.
  1269. ; Exits with es:di -> iocb; destroys ds:si, ax, bx, cx, dx, bp.
  1270.     assume    ds:nothing, es:nothing
  1271.     les    di,send_head        ; Get head segment:offset
  1272.     lds    si,es:[di].next        ; Get next segment:offset
  1273.     mov    send_head.offs, si    ; Set head offset
  1274.     mov    send_head.segm, ds    ; Set head segment
  1275.     or    es:flags[di], DONE    ; Mark done
  1276.     mov    es:ret_code[di], ah    ; Set retcode
  1277.     test    es:[di].flags,CALLME    ; Does he want an upcall?
  1278.     je    send_dequeue_1        ; No.
  1279.     push    es            ; Push iocb segment
  1280.     push    di            ;  and offset
  1281.     clc                ; Clear carry.
  1282.     mov    ax,1            ; Push a number that cant be flags.
  1283.     push    ax
  1284.     pushf                ; Save flags in case iret used.
  1285.     call    es:[di].upcall        ; Call the client.
  1286.     pop    ax            ; Pop first word.
  1287.     cmp    ax,1            ; If this is a 1, IRET was used.
  1288.     je    send_dequeue_2        ; Far return used.
  1289.     add    sp,2            ; Pop flags.
  1290. send_dequeue_2:
  1291.     pop    di            ; Pop iocb segment
  1292.     pop    es            ;  and offset
  1293. send_dequeue_1:
  1294.     ret
  1295.  
  1296.  
  1297. code    ends
  1298.  
  1299.     end    start
  1300.