home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / hamradio / s920603.zip / PCGEN.ASM < prev    next >
Assembly Source File  |  1992-05-30  |  19KB  |  877 lines

  1. ; Collection of assembler support routines for NOS
  2. ; Copyright 1991 Phil Karn, KA9Q
  3.  
  4.     .MODEL    MEMMOD,C
  5.     LOCALS
  6.     %MACS
  7.     .LALL
  8.     extrn    ctick:proc
  9.     public    eoi
  10.  
  11. ; Hardware vector for timer linkage
  12. ; We use the timer hardware channel here instead of the indirect BIOS
  13. ; channel (1ch) because the latter is sluggish when running under DoubleDos
  14. TIMEVEC    EQU    08h
  15.  
  16.     .DATA
  17.     public    Intstk,Stktop,Spsave,Sssave,Mtasker,Hashtab
  18.     extrn    Isat:word
  19. Spsave    dw    ?        ; Save location for SP during interrupts
  20. Sssave    dw    ?        ; Save location for SS during interrupts
  21. Intstk    dw    512 dup(?)    ; Interrupt working stack
  22. Stktop    equ    $        ; SP set here when entering interrupt
  23. Mtasker    db    ?        ; Type of higher multitasker, if any
  24. Hashtab    db    256 dup(?)    ; Modulus lookup table for iphash()
  25.     .CODE
  26. dbase    dw    @Data
  27. jtable    dw    l0,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11,l12,l13,l14,l15    
  28. vector    dd    ?        ; place to stash chained vector
  29. vectlo    equ    word ptr vector
  30. vecthi    equ    word ptr vector+2
  31.  
  32. ; Re-arm 8259 interrupt controller(s)
  33. ; Should be called just after taking an interrupt, instead of just
  34. ; before returning. This is because the 8259 inputs are edge triggered, and
  35. ; new interrupts arriving during an interrupt service routine might be missed.
  36. eoi    proc
  37.     cmp    Isat,1
  38.     jnz    @@1        ; Only one 8259, so skip this stuff
  39.     mov    al,0bh        ; read in-service register from
  40.     out    0a0h,al        ; secondary 8259
  41.     nop            ; settling delay
  42.     nop
  43.     nop
  44.     in    al,0a0h        ; get it
  45.     or    al,al        ; Any bits set?
  46.     jz    @@1        ; nope, not a secondary interrupt
  47.     mov    al,20h        ; Get EOI instruction
  48.     out    0a0h,al        ; Secondary 8259 (PC/AT only)
  49. @@1:    mov    al,20h        ; 8259 end-of-interrupt command
  50.     out    20h,al        ; Primary 8259
  51.     ret
  52. eoi    endp
  53.  
  54. ; common routine for interrupt return
  55. ; Note that all hardware interrupt handlers are expected to return
  56. ; the original vector found when the device first attached. We branch
  57. ; to it just after we've cleaned up here -- this implements shared
  58. ; interrupts through vector chaining. If the original vector isn't
  59. ; available, the interrupt handler must return NULL to avoid a crash!
  60. public    doret
  61.     label    doret    far
  62.     cmp    ax,0        ; is a chained vector present?
  63.     jne    @@1        ; yes
  64.     if    @Datasize NE 0
  65.         cmp    dx,ax
  66.         jne    @@1        ; yes
  67.     endif
  68.     pop    es        ; nope, return directly from interrupt
  69.     pop    di
  70.     pop    si
  71.     pop    bp
  72.     pop    dx
  73.     pop    cx
  74.     pop    bx
  75.     pop    ax
  76.     mov    ss,Sssave
  77.     mov    sp,Spsave    ; restore original stack context
  78.     pop    ds
  79.     iret
  80.  
  81. ; Code to handle vector chaining
  82. @@1:    mov    cs:vectlo,ax    ; stash vector for later branch
  83.     if    @Datasize NE 0
  84.         mov    cs:vecthi,dx
  85.     endif
  86.     pop    es
  87.     pop    di
  88.     pop    si
  89.     pop    bp
  90.     pop    dx
  91.     pop    cx
  92.     pop    bx
  93.     pop    ax
  94.     mov    ss,Sssave
  95.     mov    sp,Spsave    ; restore original stack context
  96.     pop    ds
  97.     if    @Datasize NE 0
  98.         jmp    cs:[vector]    ; jump to the original interrupt handler
  99.     else
  100.         jmp    cs:[vectlo]
  101.     endif
  102.  
  103. ; istate - return current interrupt state
  104.     public    istate
  105. istate    proc
  106.     pushf
  107.     pop    ax
  108.     and    ax,200h
  109.     jnz    @@1
  110.     ret
  111. @@1:    mov    ax,1
  112.     ret
  113. istate    endp
  114.  
  115. ; dirps - disable interrupts and return previous state: 0 = disabled,
  116. ;    1 = enabled
  117.     public dirps
  118. dirps    proc
  119.     pushf            ; save flags on stack
  120.     pop    ax        ; flags -> ax
  121.     and    ax,200h        ; 1<<9 is IF bit
  122.     jz    @@1        ; ints are already off; return 0
  123.     mov    ax,1
  124.     cli            ; interrupts now off
  125. @@1:    ret
  126. dirps    endp
  127.  
  128. ; restore - restore interrupt state: 0 = off, nonzero = on
  129.     public    restore
  130. restore    proc
  131.     arg is:word
  132.     test    is,0ffffh
  133.     jz    @@1
  134.     sti
  135.     ret
  136. @@1:    cli    ; should already be off, but just in case...
  137.     ret
  138. restore    endp
  139.  
  140.  
  141. ; multitasker types
  142. NONE        equ    0
  143. DOUBLEDOS    equ    1
  144. DESQVIEW    equ    2
  145. WINDOWS3    equ    3
  146. OS2        equ    4
  147.  
  148. ; Relinquish processor so other task can run
  149.     public    giveup
  150. giveup    proc
  151.     pushf        ;save caller's interrupt state
  152.     sti        ;re-enable interrupts
  153.     cmp    mtasker, DOUBLEDOS
  154.     jnz    @@1
  155.     mov    al,2    ; 110 ms
  156.     mov    ah,0eeh
  157.     int    21h
  158.     popf        ; restore caller's interrupt state
  159.     ret
  160.  
  161. @@1:    cmp    mtasker, DESQVIEW
  162.     jnz    @@2
  163.     mov    ax, 1000h
  164.     int    15h
  165.     popf        ; restore interrupts
  166.     ret
  167.  
  168. @@2:    cmp    mtasker, WINDOWS3
  169.     jnz    @@3
  170.     mov    ax, 1680h
  171.     int    2fh
  172.     cmp    al, 80h    ; call supported?
  173.     jz    @@3    ; nope
  174.     popf        ; yes - restore interrupts
  175.     ret
  176.  
  177. @@3:    cmp    mtasker, OS2
  178.     jnz    @@4
  179.     mov    ax, 1680h
  180.     int    2fh
  181.     popf        ; restore interrupts
  182.     ret
  183.  
  184. @@4:    hlt        ; wait for an interrupt
  185.  
  186.     popf        ; restore interrupts
  187.     ret
  188. giveup    endp
  189.  
  190. ; check for a multitasker running
  191.     public    chktasker
  192. chktasker    proc
  193.     mov    mtasker,NONE
  194.  
  195.     ; Check for OS/2
  196.     mov    ax,3000h    ; Get MS-DOS Version Number call
  197.     int    21h
  198.     cmp    al,20        ; Version 20 = OS/2 2.0
  199.     jnz    @@5
  200.     mov    mtasker, OS2
  201.     ret
  202.  
  203.       ; Check for Microsoft Windows
  204. @@5:    mov    ax,1600h
  205.  
  206.     ; Check for Microsoft Windows
  207.     mov    ax,1600h
  208.     int    2fh
  209.     cmp    al, 00h        ; 0 means windows multitasking not running
  210.     jz    @@4
  211.     cmp    al, 80h        ; ditto for 80h return
  212.     jz    @@4
  213.     mov    mtasker, WINDOWS3
  214.     ret
  215.  
  216.     ; Check for DoubleDos
  217. @@4:    mov    ah,0e4h
  218.     int    21h
  219.     cmp    al,1
  220.     jz    @@1
  221.     cmp    al,2
  222.     jnz    @@2
  223. @@1:    mov    mtasker, DOUBLEDOS
  224.     ret
  225.  
  226.     ; Check for DESQVIEW
  227. @@2:    mov    ax, 2b01h
  228.     mov    cx, 4445h
  229.     mov    dx, 5351h
  230.     int    21h
  231.     cmp    al, 0ffh
  232.     jnz    @@3
  233.     ret
  234.  
  235. @@3:    mov    mtasker, DESQVIEW
  236.     ret
  237. chktasker    endp
  238.  
  239. ; getss - Read SS for debugging purposes
  240.     public    getss
  241. getss    proc
  242.     mov    ax,ss
  243.     ret
  244. getss    endp
  245.  
  246. ; clockbits - Read low order bits of timer 0 (the TOD clock)
  247. ; This works only for the 8254 chips used in ATs and 386s.
  248. ;
  249. ; The timer runs in mode 3 (square wave mode), counting down
  250. ; by twos, twice for each cycle. So it is necessary to read back the
  251. ; OUTPUT pin to see which half of the cycle we're in. I.e., the OUTPUT
  252. ; pin forms the most significant bit of the count. Unfortunately,
  253. ; the 8253 in the PC/XT lacks a command to read the OUTPUT pin...
  254. ;
  255. ; The PC's clock design is soooo brain damaged...
  256.  
  257.     public    clockbits
  258. clockbits    proc
  259.     mov    al,0c2h    ; latch timer 0 count and status for reading
  260.     pushf
  261.     cli        ; make chip references atomic
  262.     out    43h,al    ; send latch command
  263.     in    al,40h    ; get status of timer 0
  264.     mov    bl,al    ; save status
  265.     in    al,40h    ; get lsb of count
  266.     mov    ah,al    ; save lsb
  267.     in    al,40h    ; get msb of count
  268.     popf        ; no more chip references
  269.     and    bl,80h    ; we're only interested in the OUT bit
  270.     xchg    ah,al    ; ax = count in correct order
  271.     shr    ax,1    ; count /= 2
  272.     jz    @@3    ; zero count requires carry propagation
  273. @@2:    or    ah,bl    ; combine with OUT bit as most sig bit of count
  274.     ret
  275. @@3:    xor    bl,80h    ; propagate carry by toggling OUT bit when cnt == 0
  276.     or    ah,bl    ; combine with !OUT bit as most sig bit of count
  277.     ret
  278.  
  279. clockbits    endp
  280.  
  281. ; Internet checksum subroutine
  282. ; Compute 1's-complement sum of data buffer
  283. ; Uses an unwound loop inspired by "Duff's Device" for performance
  284. ;
  285. ; Called from C as
  286. ; unsigned short
  287. ; lcsum(buf,cnt)
  288. ; unsigned short *buf;
  289. ; unsigned short cnt;
  290.     public    lcsum
  291. lcsum    proc
  292.     arg    buf:ptr,cnt:word
  293.  
  294.     if    @Datasize NE 0
  295.         uses    ds,si
  296.         lds    si,buf    ; ds:si = buf
  297.     else
  298.         uses    si
  299.         mov    si,buf    ; ds:si = buf (ds already set)
  300.     endif
  301.  
  302.     mov    cx,cnt        ; cx = cnt
  303.     cld            ; autoincrement si
  304.     mov    ax,cx
  305.     shr    cx,1        ; cx /= 16, number of loop iterations
  306.     shr    cx,1
  307.     shr    cx,1
  308.     shr    cx,1
  309.  
  310.     inc    cx        ; make fencepost adjustment for 1st pass
  311.     and    ax,15        ; ax = number of words modulo 16
  312.     shl    ax,1        ; *=2 for word table index
  313.     lea    bx,jtable    ; bx -> branch table
  314.     add    bx,ax        ; index into jump table
  315.     clc            ; initialize carry = 0
  316.     mov    dx,0        ; clear accumulated sum
  317.     jmp    word ptr cs:[bx]    ; jump into loop
  318.  
  319. ; Here the real work gets done. The numeric labels on the lodsw instructions
  320. ; are the targets for the indirect jump we just made.
  321. ;
  322. ; Each label corresponds to a possible remainder of (count / 16), while
  323. ; the number of times around the loop is determined by the quotient.
  324. ;
  325. ; The loop iteration count in cx has been incremented by one to adjust for
  326. ; the first pass.
  327. deloop:    lodsw
  328.     adc    dx,ax
  329. l15:    lodsw
  330.     adc    dx,ax
  331. l14:    lodsw
  332.     adc    dx,ax
  333. l13:    lodsw
  334.     adc    dx,ax
  335. l12:    lodsw
  336.     adc    dx,ax
  337. l11:    lodsw
  338.     adc    dx,ax
  339. l10:    lodsw
  340.     adc    dx,ax
  341. l9:    lodsw
  342.     adc    dx,ax
  343. l8:    lodsw
  344.     adc    dx,ax
  345. l7:    lodsw
  346.     adc    dx,ax
  347. l6:    lodsw
  348.     adc    dx,ax
  349. l5:    lodsw
  350.     adc    dx,ax
  351. l4:    lodsw
  352.     adc    dx,ax
  353. l3:    lodsw
  354.     adc    dx,ax
  355. l2:    lodsw
  356.     adc    dx,ax
  357. l1:    lodsw
  358.     adc    dx,ax
  359. l0:    loop    deloop        ; :-)
  360.  
  361.     adc    dx,0        ; get last carries
  362.     adc    dx,0
  363.     mov    ax,dx        ; result into ax
  364.     xchg    al,ah        ; byte swap result (8088 is little-endian)
  365.     ret
  366. lcsum    endp
  367.  
  368. ; Link timer handler into timer chain
  369. ; Arg == address of timer handler routine
  370. ; MUST be called exactly once before uchtimer is called!
  371.  
  372. toff    dw    ?        ; save location for old vector
  373. tseg    dw    ?        ;  must be in code segment
  374.  
  375.     public    chtimer
  376. chtimer    proc
  377.     arg    vec:far ptr
  378.     uses    ds
  379.  
  380.     mov    ah,35h        ; get current vector
  381.     mov    al,TIMEVEC
  382.     int    21h        ; puts vector in es:bx
  383.     mov    cs:tseg,es    ; stash
  384.     mov    cs:toff,bx
  385.  
  386.     mov    ah,25h
  387.     mov    al,TIMEVEC
  388.     lds    dx,vec        ; ds:si = vec
  389.  
  390.     int    21h        ; set new vector
  391.     ret
  392. chtimer    endp
  393.  
  394. ; unchain timer handler from timer chain
  395. ; MUST NOT be called before chtimer!
  396.     public    uchtimer
  397. uchtimer    proc
  398.     uses    ds
  399.  
  400.     mov    ah,25h
  401.     mov    al,TIMEVEC
  402.     mov    dx,toff
  403.     mov    ds,tseg
  404.     int    21h        ; restore old vector
  405.     ret
  406. uchtimer    endp
  407.  
  408. ; Clock tick interrupt handler. Note the use of "label" rather than "proc"
  409. ; here, necessitated by the fact that "proc" automatically generates BP-saving
  410. ; code that we don't want here.
  411.  
  412.     public    btick
  413.     label    btick    far
  414.  
  415.     pushf
  416.     push    ds
  417.     cli
  418.     mov    ds,cs:dbase    ; establish interrupt data segment
  419.  
  420.     mov    Sssave,ss    ; stash user stack context
  421.     mov    Spsave,sp
  422.  
  423.     mov    ss,cs:dbase
  424.     lea    sp,Stktop
  425.  
  426.     push    ax        ; save user regs on interrupt stack
  427.     push    bx
  428.     push    cx
  429.     push    dx
  430.     push    bp
  431.     push    si
  432.     push    di
  433.     push    es
  434.  
  435.     call    ctick
  436.  
  437.      pop    es
  438.     pop    di
  439.     pop    si
  440.     pop    bp
  441.     pop    dx
  442.     pop    cx
  443.     pop    bx
  444.     pop    ax
  445.     mov    ss,Sssave
  446.     mov    sp,Spsave    ; restore original stack context
  447.     pop    ds
  448.     popf
  449.     jmp    dword ptr [toff]        ; link to previous vector
  450.  
  451. ; Convert 32-bit int in network order to host order (dh, dl, ah, al)
  452. ; Called from C as
  453. ; int32 get32(char *cp);
  454.  
  455.     public    get32
  456. get32    proc
  457.     arg    cp:ptr
  458.     if    @Datasize NE 0
  459.         uses    ds,si
  460.         lds    si,cp    ; ds:si = cp
  461.     else
  462.         uses    si
  463.         mov    si,cp    ; ds:si = cp (ds already set)
  464.     endif
  465.  
  466.     cld
  467.     lodsw
  468.     mov    dh,al    ; high word to dx, a-swapping as we go
  469.     mov    dl,ah
  470.     lodsw
  471.     xchg    al,ah    ; low word stays in ax, just swap
  472.     ret
  473. get32    endp
  474.  
  475. ; Convert 16-bit int in network order to host order (ah, al)
  476. ; Called from C as
  477. ; int16 get16(char *cp);
  478.  
  479.     public    get16
  480. get16    proc
  481.     arg    cp:ptr
  482.     if    @Datasize NE 0
  483.         uses    ds,si
  484.         lds    si,cp    ; ds:si = cp
  485.     else
  486.         uses    si
  487.         mov    si,cp    ; ds:si = cp (ds already set)
  488.     endif
  489.  
  490.     lodsw        ; note: direction flag is don't-care
  491.     xchg    al,ah    ; word stays in ax, just swap
  492.     ret
  493. get16    endp
  494.  
  495. ; Convert 32-bit int to network order, returning new pointer
  496. ; Called from C as
  497. ; char *put32(char *cp,int32 x);
  498.  
  499.     public    put32
  500. put32    proc
  501.     arg    cp:ptr,x:dword
  502.     if    @Datasize NE 0
  503.         uses    ds,di
  504.         les    di,cp    ; es:di = cp
  505.         mov    ax,ss    ; our parameter is on the stack, and ds might not
  506.         mov    ds,ax    ;   be pointing to ss.
  507.     else
  508.         uses    di
  509.         mov    di,cp    ; es:di = cp
  510.         mov    ax,ds    ; point es at data segment
  511.         mov    es,ax
  512.     endif
  513.  
  514.     cld
  515.     mov    ax,word ptr (x+2)    ; read high word of machine version
  516.     xchg    ah,al            ; swap bytes
  517.     stosw                ; output in network order
  518.     mov    ax,word ptr x        ; read low word of machine version
  519.     xchg    ah,al            ; swap bytes
  520.     stosw                ; put in network order
  521.  
  522.     mov    ax,di    ; return incremented output pointer
  523.     if    @Datasize NE 0
  524.         mov    dx,es    ; upper half of pointer
  525.     endif
  526.     ret
  527. put32    endp
  528.  
  529. ; Convert 16-bit int to network order, returning new pointer
  530. ; Called from C as
  531. ; char *put16(char *cp,int16 x);
  532.  
  533.     public    put16
  534. put16    proc
  535.     arg    cp:ptr,x:word
  536.     uses    di
  537.     if    @Datasize NE 0
  538.         les    di,cp    ;es:di = cp
  539.     else
  540.         mov    di,cp    ; es:di = cp
  541.         mov    ax,ds
  542.         mov    es,ax
  543.     endif
  544.     cld
  545.     mov    ax,x    ; fetch source word in machine order
  546.     xchg    ah,al    ; swap bytes
  547.     stosw        ; save in network order
  548.     mov    ax,di    ; return new output pointer to user
  549.     if    @Datasize NE 0
  550.         mov    dx,es    ; upper half of pointer
  551.     endif
  552.     ret
  553. put16    endp
  554.  
  555. ; kbraw - raw, nonblocking read from console
  556. ; If character is ready, return it; if not, return -1
  557.     public    kbraw
  558. kbraw    proc
  559.     mov    ah,06h    ; Direct Console I/O
  560.     mov    dl,0ffh    ; Read from keyboard
  561.     int    21h    ; Call DOS
  562.     jz    @@1    ; zero flag set -> no character ready
  563.     mov    ah,0    ; valid char is 0-255
  564.     ret
  565. @@1:    mov    ax,-1    ; no char, return -1
  566.     ret
  567. kbraw    endp
  568.  
  569. if    @CPU AND 2
  570. ; fast I/O buffer routines
  571. ; version for 80186, 286, 386 (uses ins, outs instructions)
  572.  
  573. ; outbuf - put a buffer to an output port
  574.     public    outbuf
  575. outbuf    proc
  576.     arg    port:word,buf:ptr,cnt:word
  577.     if    @Datasize NE 0
  578.         uses    ds,si
  579.         lds    si,buf    ; ds:si = buf
  580.     else
  581.         uses    si
  582.         mov    si,buf    ;ds:si = buf (ds already set)
  583.     endif
  584.     mov    dx,port
  585.     mov    cx,cnt
  586.     cld
  587.     rep outsb        ; works only on PC/AT (80286)
  588.     mov    dx,ds
  589.     mov    ax,si        ; return pointer just past end of buffer
  590.     ret
  591. outbuf    endp
  592.  
  593. ; inbuf - get a buffer from an input port
  594.     public    inbuf
  595. inbuf    proc
  596.     arg    port:word,buf:ptr,cnt:word
  597.     uses    di
  598.     if    @Datasize NE 0
  599.         les    di,buf        ; es:di = buf
  600.     else
  601.         mov    di,buf        ; es:di = buf
  602.         mov    ax,ds
  603.         mov    es,ax
  604.     endif
  605.     mov    dx,port
  606.     mov    cx,cnt
  607.     cld
  608.     rep insb        ; works only on PC/AT (80286)
  609.     mov    dx,es
  610.     mov    ax,di        ; return pointer just past end of buffer
  611.     ret
  612. inbuf    endp
  613.  
  614. else
  615.  
  616. ; fast buffer I/O routines
  617. ; version for 8086/8
  618.  
  619. ; outbuf - put a buffer to an output port
  620.     public    outbuf
  621. outbuf    proc
  622.     arg    port:word,buf:ptr,cnt:word
  623.     if    @Datasize NE 0
  624.         uses    ds,si
  625.         lds    si,buf    ; ds:si = buf
  626.     else
  627.         uses    si
  628.         mov    si,buf    ; ds:si = buf (ds already set)
  629.     endif
  630.  
  631.     mov    dx,port
  632.     mov    cx,cnt
  633.     cld
  634.  
  635. ; If buffer doesn't begin on a word boundary, send the first byte
  636.     test    si,1    ; (buf & 1) ?
  637.     jz    @@even ; no
  638.     lodsb        ; al = *si++;
  639.     out    dx,al    ; out(dx,al);
  640.     dec    cx    ; cx--;
  641.     mov    cnt,cx    ; save for later test
  642. @@even:
  643.     shr    cx,1    ; cx = cnt >> 1; (convert to word count)
  644. ; Do the bulk of the buffer, a word at a time
  645.     jcxz    @@nobuf    ; if(cx != 0){
  646. @@deloop:
  647.     lodsw        ; do { ax = *si++; (si is word pointer)
  648.     out    dx,al    ; out(dx,lowbyte(ax));
  649.     mov    al,ah
  650.     out    dx,al    ; out(dx,hibyte(ax));
  651.     loop    @@deloop    ; } while(--cx != 0); }
  652. ; now check for odd trailing byte
  653. @@nobuf:
  654.     mov    cx,cnt
  655.     test    cx,1
  656.     jz    @@cnteven
  657.     lodsb        ; al = *si++;
  658.     out    dx,al
  659. @@cnteven:
  660.     mov    dx,ds
  661.     mov    ax,si        ; return pointer just past end of buffer
  662.     ret
  663. outbuf    endp
  664.  
  665. ; inbuf - get a buffer from an input port
  666.     public    inbuf
  667. inbuf    proc
  668.     arg port:word,buf:ptr,cnt:word
  669.     uses    di
  670.     if    @Datasize NE 0
  671.         les    di,buf    ; es:di = buf
  672.     else
  673.         mov    di,buf    ; es:di = buf
  674.         mov    ax,ds
  675.         mov    es,ax
  676.     endif
  677.     mov    dx,port
  678.     mov    cx,cnt
  679.     cld
  680.  
  681. ; If buffer doesn't begin on a word boundary, get the first byte
  682.     test    di,1    ; if(buf & 1){
  683.     jz    @@bufeven ;
  684.     in    al,dx    ; al = in(dx);
  685.     stosb        ; *di++ = al
  686.     dec    cx    ; cx--;
  687.     mov    cnt,cx    ; cnt = cx; } save for later test
  688. @@bufeven:
  689.     shr    cx,1    ; cx = cnt >> 1; (convert to word count)
  690. ; Do the bulk of the buffer, a word at a time
  691.     jcxz    @@nobuf    ; if(cx != 0){
  692. @@deloop:
  693.     in    al,dx    ; do { al = in(dx);
  694.     mov    ah,al
  695.     in    al,dx    ; ah = in(dx);
  696.     xchg    al,ah
  697.     stosw        ; *si++ = ax; (di is word pointer)
  698.     loop    @@deloop    ; } while(--cx != 0);
  699. ; now check for odd trailing byte
  700. @@nobuf:
  701.     mov    cx,cnt
  702.     test    cx,1
  703.     jz    @@cnteven
  704.     in    al,dx
  705.     stosb        ; *di++ = al
  706. @@cnteven:
  707.     mov    dx,es
  708.     mov    ax,di        ; return pointer just past end of buffer
  709.     ret
  710. inbuf    endp
  711.  
  712. endif
  713.  
  714.     public    longdiv
  715.  
  716. ; long unsigned integer division - divide an arbitrary length dividend by
  717. ; a 16-bit divisor. Replaces the dividend with the quotient and returns the
  718. ; remainder. Called from C as
  719. ;
  720. ; unsigned short
  721. ; longdiv(unsigned short divisor,int cnt,unsigned short *dividend);
  722. ;
  723. ;Register usage:
  724. ; di - divisor
  725. ; si - pointer into dividend array
  726. ; cx - loop counter, initialized to the number of 16-bit words in the dividend
  727. ; ax - low word of current dividend before each divide, current quotient after
  728. ; dx - remainder from previous divide carried over, becomes high word of
  729. ;      dividend for next divide
  730.  
  731. longdiv    proc
  732.     arg    divisor:word,cnt:word,dividend:ptr
  733.     if    @Datasize NE 0
  734.         uses    ds,si,di
  735.         lds    si,dividend
  736.     else
  737.         uses    si,di
  738.         mov    si,dividend    ;si -> dividend array
  739.     endif
  740.  
  741.     cmp    divisor,0        ; divisor == 0?
  742.     jne    @2            ; no, ok
  743.     xor    ax,ax            ; yes, avoid divide-by-zero trap
  744.     jmp    short @1
  745.  
  746. @2:    mov    dx,0            ; init remainder = 0
  747.     mov    cx,cnt            ; init cnt
  748.     mov    di,divisor        ; cache divisor in register
  749.  
  750. @@deloop:
  751.     mov    ax,word ptr [si]    ; fetch current word of dividend
  752.     cmp    ax,0            ; dividend == 0 ?
  753.     jne    @7            ; nope, must do division
  754.     cmp    dx,0            ; remainder also == 0?
  755.     je    @4            ; yes, skip division, continue
  756.  
  757. @7:    div    di            ; do division
  758.     mov    word ptr [si],ax    ; save quotient
  759.  
  760. @4:    inc    si            ; next word of dividend
  761.     inc    si
  762.     loop     @@deloop
  763.  
  764.     mov    ax,dx            ; return last remainder
  765. @1:    ret
  766.  
  767. longdiv    endp
  768.  
  769. ; long unsigned integer multiplication - multiply an arbitrary length
  770. ; multiplicand by a 16-bit multiplier, leaving the product in place of
  771. ; the multipler, returning the carry. Called from C as
  772. ;
  773. ; unsigned short
  774. ; longmul(unsigned short multiplier,int cnt,unsigned short *multiplier);
  775. ;
  776. ; Register usage:
  777. ; di = multiplier
  778. ; si = pointer to current word of multiplicand
  779. ; bx = carry from previous round
  780. ; cx = count of words in multiplicand
  781. ; dx,ax = scratch for multiply
  782.  
  783.     public longmul
  784. longmul    proc    far
  785.     arg    multiplier:word,n:word,multiplicand:ptr
  786.     if    @Datasize NE 0
  787.         uses    ds,si,di
  788.         lds    si,multiplicand
  789.     else
  790.         uses    si,di
  791.         mov    si,multiplicand    ; si -> multiplicand array
  792.     endif
  793.  
  794.     mov    di,multiplier        ; cache multiplier in register
  795.     xor    bx,bx            ; init carry = 0
  796.     mov    cx,n            ; fetch n
  797.     mov    ax,cx
  798.     shl    ax,1            ; *2 = word offset
  799.     add    si,ax            ; multiplicand += n
  800.  
  801. @@deloop:
  802.     dec    si
  803.     dec    si            ; work from right to left
  804.     mov    ax,word ptr [si]    ; fetch current multiplicand
  805.     or    ax,ax            ; skip multiply if zero
  806.     jz    @@nomult
  807.     mul    di            ; dx:ax <- ax * di
  808. @@nomult:
  809.     add    ax,bx            ; add carry from previous multiply
  810.     mov    word ptr [si],ax    ; save low order word of product
  811.     mov    bx,0            ; clear previous carry, leaving CF alone
  812.     adc    bx,dx            ; save new carry
  813.     xor    dx,dx            ; clear in case we skip the next mult
  814.     loop    @@deloop
  815.  
  816.     mov    ax,bx            ; return final carry
  817.     ret
  818. longmul    endp
  819.  
  820. ifdef    notdef
  821. ; divide 32 bits by 16 bits, returning both quotient and remainder
  822. ; This allows C programs that need both to avoid having to do two divisions
  823. ;
  824. ; Called from C as
  825. ;    long divrem(dividend,divisor)
  826. ;    long dividend;
  827. ;    short divisor;
  828. ;
  829. ;    The quotient is returned in the low 16 bits of the result,
  830. ;    and the remainder is returned in the high 16 bits.
  831.  
  832.     public    divrem
  833. divrem    proc
  834.     arg    dividend:dword,divisor:word
  835.     mov    ax,word ptr dividend
  836.     mov    dx,word ptr (dividend+2)
  837.     div    divisor
  838.     ret
  839. divrem    endp
  840. endif    
  841.  
  842. ; General purpose hash function for IP addresses
  843. ; Uses lookup table Hashtab[] initialized in ip.c
  844. ; Called from C as
  845. ; char hash_ip(int32 ipaddr);
  846.  
  847.     public hash_ip
  848. hash_ip    proc
  849.     arg    ipaddr:dword
  850.     lea    bx,Hashtab
  851.     mov    ax,word ptr ipaddr
  852.     xor    ax,word ptr (ipaddr+2)
  853.     xor    al,ah
  854.     xlat
  855.     xor    ah,ah
  856.     ret
  857. hash_ip    endp
  858.  
  859. ; Compute int(log2(x))
  860. ; Called from C as
  861. ; int log2(int16 x);
  862.  
  863.     public    log2
  864. log2    proc
  865.     arg    x:word
  866.     mov    cx,16
  867.     mov    ax,x
  868. @@2:    rcl    ax,1 
  869.     jc    @@1
  870.     loop    @@2
  871. @@1:    dec    cx
  872.     mov    ax,cx
  873.     ret
  874. log2    endp
  875.     end
  876.