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