home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 024 / psi110g.zip / PCGEN.ASM < prev    next >
Assembly Source File  |  1994-08-26  |  24KB  |  892 lines

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