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