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