Xinu7/src/lib/libeis/div.s

/
/ PDP-11 DIVIDE ROUTINE
/
/ This routine will divide a 32-bit signed number by a 16-bit signed number using
/ two's complement arithmetic. It also sets the condition codes properly upon exit
/ in the location spsw.
/
/ DEFINITION:
/ Divide                DO                                      071RSS
/ Operation:    R,Rv1 <- R,Rv1 / (src)
/ Condition     N: set if quotient < 0 (unspecified if V = 1).
/ Codes:        Z: set if quotient = 0 (unspecified if V = 1).
/               V: set if source = 0 or if quotient cannot be represented as a
/                  16-bit 2's complement number. R,Rv1 are unpredictable if V
/                  is set.
/               C: set if divide by 0 attempted.
/ Description:  ... The 32-bit 2's complement integer in R and Rv1 is divided by
/               the source operand. The quotient is left in R; the remainder in
/               Rv1 is of the same sign as the dividend. R must be even.
/
/ (copied from MICROCOMPUTERS AND MEMORIES, c. 1981 by Digital Equipment
/ Corporation; pp. 103 - 104)
/
/ EXAMPLE:
/       4 DIV  3 =  1                            4 MOD  3 =  1
/      -4 DIV -3 =  1                           -4 MOD -3 = -1
/       4 DIV -3 = -1                            4 MOD -3 =  1
/      -4 DIV  3 = -1                           -4 MOD  3 = -1
/
/ ALGORITHM:
/   The algorithm is the conventional shift-and-subtract one. The following C
/ version is meant to be a guide ONLY; the code below is a hand-compilation of
/ this program (essentially). (The given version ignores C parameter passing
/ conventions.)
/
/ div(a, b, q, r, psw)
/ long a;                         /* dividend */
/ long b;                         /* divisor */
/ int q;                          /* quotient */
/ int r;                          /* remainder */
/ int psw;                        /* processor status word */
/ {
/         int aneg, bneg;         /* 1 if corresponding variable is < 0 */
/         int c;                  /* count of shifts of quotient */
/
/         q = c = aneg = bneg = 0;/* initialize everything */
/         psw &= ~017;
/                                 /* handle 0 divisor or dividend */
/         if (b == 0L){ psw |= 03; return; }
/         if (a == 0L){ psw |= 07; return; }
/                                 /* handle negative divisor, dividend */
/         if (b < 0L){ b = -b; bneg++; }
/         if (a < 0L){ a = -a; aneg++; }
/                                 /* compute initial divisor shift */
/         while (a > b && !(b & 020000000000L)){
/                 b <<= 1;
/                 c++;
/         }
/                                 /* division loop */
/         while(1){
/                 q <<= 1;                /* one more subtraction */
/                 if (q & 0100000) psw |= 02;             /* flag overflow */
/                 if (b <= a){            /* can subtract */
/                         q += 1;                         /* reset quotient */
/                         if (q & 0100000) psw |= 02;     /* flag overflow */
/                         if ((a -= b) == 0L) break;      /* exact division */
/                 }
/                 if (--c < 0) break;     /* see if all shifts done */
/                 b >>= 1;                /* nope - halve divisor */
/         }
/                                 /* set remainder sign properly */
/         if (aneg != 0) a = -a;
/         r = (int) a;
/                                 /* shift quotient as required */
/         while(--c >= 0){
/                 q <<= 1;                /* a shift left */
/                 if (q & 0100000) psw |= 02;     /* flag overflow */
/         }
/                                 /* set quotient sign */
/         if (aneg != bneg){ q = -q; psw |= 010; }
/ }
/
/ CALLING SEQUENCE:
/ code to simulate the instruction "div A,r0":
/       mfps    -(sp)                   / push psw onto stack
/       mov     A,-(sp)                 / push divisor onto stack
/       mov     r0,-(sp)                / push high word of dividend onto stack
/       mov     r1,-(sp)                / push low word of dividend onto stack
/       jsr     pc,over2                / call the simulation routine
/       mov     (sp)+,r1                / save the remainder
/       mov     (sp)+,r0                / save the quotient
/       tst     (sp)+                   / pop garbage word
/       mtps    (sp)+                   / put in the new psw
/
/ WARNING:
/ THIS ROUTINE DOES NOT FOLLOW THE CALLING CONVENTIONS OF C. DO NOT CALL THIS
/ USING THE USUAL C CONVENTIONS; USE THE CALLING SEQUENCE GIVEN ABOVE.
/
/ ASSUMPTIONS:
/ the stack must be set up correctly
/ the routine must be called via "jsr pc,over2" since it uses "rts pc"
/       to return
/
/ AUTHOR:
/ Matt Bishop
/ Department of Computer Sciences
/ Purdue University
/ West Lafayette, IN  47907
/
/ Version 1, October 30, 1981
/
/
/ CONSTANTS
lr=sp           / linkage register
                / stack allocation ... parameter variables
                /       position on stack after r0 - r5 are pushed
sinpsw=24               / psw (on entry)
sotpsw=24               / psw (on exit)
sblow=22                / low word of divisor (on entry); ignored (on exit)
sahigh=20               / high word of dividend (on entry)
squot=20                / quotient (on exit)
salow=16                / low word of dividend (on entry); remainder (on exit)
                / stack allocation ... temporaries (ie., local variables)
saneg=-2                / 1 if dividend negative
sbneg=-4                / 1 if divisor negative
                / register allocation
rahigh=r0               / high word of dividend
ralow=r1                / low word of dividend
rbhigh=r2               / high word of divisor
rblow=r3                / low word of divisor
rcount=r4               / number of times divisor shifted
rquot=r5                / quotient
/
/*** declare the routine name to be global so anyone can use it
.globl  over2
.text
over2:
/*** push the registers onto the stack
        mov     r0,-(lr)
        mov     r1,-(lr)
        mov     r2,-(lr)
        mov     r3,-(lr)
        mov     r4,-(lr)
        mov     r5,-(lr)
/*** set up registers and initialize condition codes
        clr     rquot                   / no quotient yet
        clr     saneg(lr)               / for now, they're positive
        clr     sbneg(lr)
        clr     rcount                  / no shifts yet
        bic     $17,sinpsw(lr)          / clear condition codes
        mov     sahigh(lr),rahigh       / load dividend and divisor into
        mov     salow(lr),ralow         /   the right registers (note the high
        clr     rbhigh                  /     word of the divisor is 0)
        mov     sblow(lr),rblow
/*** see if either divisor or dividend is 0
        bne     chkdnd                  / high word of divisor is 0
        bis     $3,sinpsw(lr)           / flag a divide by 0
        br      out                     / adios, amigo!
chkdnd: tst     rahigh                  / see if both high and low words of the
        bne     negdiv                  /   dividend are 0
        tst     ralow
        bne     negdiv
        bis     $7,sinpsw(lr)           / set the Z bit (quotient, remainder 0)
        br      out                     / au revoir, mon ami!
/*** now do the signs
negdiv: tst     rblow                   / again, we need only look at the low
        bge     negdnd                  /   word of the divisor
        neg     rblow                   / negate it
        inc     sbneg(lr)               / set flag that divisor is negative
negdnd: tst     rahigh                  / just check the high word of the divi-
        bge     inicnt                  /   dend (it's double precision)
        neg     rahigh                  / a double precision negation
        neg     ralow
        sbc     rahigh
        inc     saneg(lr)               / set flag that dividend is negative
/*** figure out the first subtraction
inicnt: cmp     rahigh,rbhigh           / see if dividend is bigger than divisor
        blt     divlop                  /   if so, double divisor until it is
        bgt     bittst                  /     as big as dividend; then run the
        cmp     ralow,rblow             /       division loop
        blos    divlop
bittst: bit     $100000,rbhigh          / see if MSB of divisor set; if so, can't
        bne     divlop                  /   shift, so try dividing
        asl     rblow                   / this doubles the divisor
        rol     rbhigh
        inc     rcount                  / keep track of how many times divisor
        br      inicnt                  /   was doubled
/*** this next loop does the actual division
divlop: asl     rquot                   / one more division made, so double
        bit     $100000,rquot           /   the quotient. if MSB overflow just
        beq     noov1                   /     occurred, so set C bit, and keep
        bis     $2,sinpsw(lr)           /       on truckin' ...
noov1:  cmp     rbhigh,rahigh           / if divisor is bigger than dividend
        blo     incquo                  /   here, what's left is the remainder
        bhi     deccnt                  /     since no more subtractions are
        cmp     rblow,ralow             /       possible
        bhi     deccnt
incquo: inc     rquot                   / the divisor can be subtracted from the
        bit     $100000,rquot           /   dividend; do it and check for over-
        beq     noov2                   /     flow (process if present)
        bis     $2,sinpsw(lr)
noov2:  sub     rblow,ralow             / this does a double precision
        sbc     rahigh                  /   subtraction
        sub     rbhigh,rahigh
        bne     deccnt                  / if the dividend is 0, done with the
        tst     ralow                   /   division loop
        beq     setsgn
deccnt: dec     rcount                  / divisor must be shifted as many times as
        blt     setsgn                  /   there were initial shifts; here,
        asr     rbhigh                  /     chalk up another such shift
        ror     rblow
        br      divlop
/*** this part sets the sign of the remainder as required
setsgn: tst     saneg(lr)               / make the remainder have the same
        beq     fixquo                  /   sign as the dividend (division def-
        neg     ralow                   /     inition, see description)
/*** this makes the quotient the right size
fixquo: dec     rcount                  / now shift the quotient left until all
        blt     quosgn                  /   initial shifts are covered
        asl     rquot
        bit     $100000,rquot           / if MSB is set, there is overflow;
        beq     fixquo                  /   handle it
        bis     $2,sinpsw(lr)
        br      fixquo
/*** set the sign of the quotient
quosgn: cmp     saneg(lr),sbneg(lr)     / if divisor and dividend have the same
        beq     out                     /   sign, the quotient is positive; else,
        neg     rquot                   /     it is negative (arithmetic
        bis     $10,sinpsw(lr)          /       definition of division)
/*** restore the registers after saving results
out:    mov     ralow,salow(lr)         / save remainder
        mov     rquot,squot(lr)         / save quotient
        mov     sinpsw(lr),sotpsw(lr)   / save psw
        mov     (lr)+,r5                / restore registers
        mov     (lr)+,r4
        mov     (lr)+,r3
        mov     (lr)+,r2
        mov     (lr)+,r1
        mov     (lr)+,r0
        rts     pc                      / do svidanya, tovarishch!
.data