Xinu7/src/lib/libeis/ash.s

/
/ PDP-11 ARITHMETIC SHIFT ROUTINE
/
/ This routine will arithmetically shift a 16-bit number up to 31 bits left
/ or 32 bits right. It also sets condition codes properly upon exit in the
/ location psw.
/
/ DEFINITION:
/ Arithmetic Shift      DO                                      072RSS
/ Operation:    R <- R shifted arithmetically NN places to the right or left
/               where NN = (src)<5:0>
/ Condition     N: set if result < 0
/ Codes:        Z: set if result = 0
/               V: set if sign of register changed during shift
/               C: loaded from last bit shifted out of register. Cleared if
/                  NN = 0.
/ Description:  ... The contents of the register are shifted right or left
/               the number of times specified by the source operand. The
/               shift count is taken as the low-order 6 bits of the source
/               operand. This number ranges from -32 to +31. Negative is a
/               right shift and positive is a left shift.
/
/ (copied from MICROCOMPUTERS AND MEMORIES, c. 1981 by Digital Equipment
/ Corporation; p. 89)
/
/ ALGORITHM:
/   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.)
/
/ ash(n, s, psw)
/ int n;                /* number to be shifted */
/ int s;                /* number of shifts */
/ int psw;              /* processor status word */
/ {
/       int origs;      /* original shift count */
/       int scnt;       /* sign bit count */
/
/       psw &= ~017;    /* initialize */
/       s &= ~077;
/       scnt = 0;
/                       /* do the shifting */
/       if (s == 0)     /* no shift */
/               psw |= 01;
/       else if (s > 31){    /* negative shift */
/               origs = s = 64 - s;
/               while(s-- > 0){
/                       n >>= 1;        /* remember, we do have this */
/                       scnt += (n < 0);
/               }
/               "if C bit set, psw |= 01";
/       }
/       else{           /* positive shift */
/               origs = s;
/               while(s-- > 0){
/                       n <<= 1;        /* remember, we do have this */
/                       scnt += (n < 0);
/               }
/               "if C bit set, psw |= 01";
/       }
/                       /* set V bit */
/       if (scnt != 0 && scnt != origs)
/               psw |= 02;
/                       /* set sign */
/       if (n < 0) psw |= 010;
/       else if (n == 0) psw |= 04;
/ }
/
/ CALLING SEQUENCE:
/ code to simulate the instruction "ash A,r0":
/       mfps    -(sp)                   / get psw
/       mov     A,-(sp)                 / push shift count onto the stack
/       mov     r0,-(sp)                / push number to be shifted onto the stack
/       jsr     pc,shift2               / call the simulation routine
/       mov     (sp)+,r0                / save the result
/       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,shift2" 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 - r4 are pushed
sinpsw=20               / psw (on entry)
sotpsw=20               / psw (on exit)
sshift=16               / number of bits to be shifted (on entry)
snumbr=14               / number to be shifted (on entry); result (on exit)
                / register allocation
rshift=r0               / number of bits remaining to be shifted
rnumbr=r1               / number being shifted
rsign=r2                / sum of sign bits of previous shifts
rpsw=r3                 / psw
rtemp=r4                / used to save number of bits to be shifted
/
/*** declare the routine name to be global so anyone can use it
.globl shift2
.text
shift2:
/*** push the registers onto the stack
        mov     r0,-(lr)
        mov     r1,-(lr)
        mov     r2,-(lr)
        mov     r3,-(lr)
        mov     r4,-(lr)                / note r5 is not used
/*** set up registers and initialize condition codes
        mov     sinpsw(lr),rpsw         / get psw
        bic     $17,rpsw                / clear its condition codes
        clr     rsign                   / no sign bits yet
        mov     snumbr(lr),rnumbr       / get number to be shifted
        mov     sshift(lr),rtemp        / get the shift count
        bic     $177700,rtemp
        beq     setsgn                  / if 0, done
        cmp     rtemp,$31               / treat as 2's complement 6-bit number
        ble     possrt                  / if <= 31, shift left that many bits
        mov     $100,rshift             / if > 31, treat as negative; subtract
        sub     rtemp,rshift            /   from 64 to get number of bits to be
        mov     rshift,rtemp            /     shifted right; save count
/*** negative (right) shift
neglop: asr     rnumbr                  / shift 1 bit right
                                        / note the sign will never change as the
                                        /   sign bit is replicated
        sob     rshift,neglop           / go back unless done
        br      setc                    / set C bit properly
/*** positive (left) shift
possrt: mov     rtemp,rshift            / save original shift count
        inc     rtemp                   / get final shift total
        tst     rnumbr
        bpl     poslop                  / add sign bit to rsign
        inc     rsign
poslop: asl     rnumbr                  / shift 1 bit left
        bpl     poslnd                  / add sign bit to rsign
        inc     rsign
poslnd: sob     rshift,poslop           / go back unless done
setc:   bcc     setv                    / last bit shifted a 1
        bis     $1,rpsw                 / if so, set C bit
/*** set V bit correctly
setv:   tst     rsign                   / now for the V bit; no sign change if
        beq     setsgn                  /   rnumbr was always positive (rsign
        cmp     rsign,rtemp             /     == 0) or always negative (rsign
        beq     setsgn                  /       == original shift count + 1); else,
        bis     $2,rpsw                 /         sign bit changed
/*** set the sign bits correctly
setsgn: tst     rnumbr                  / get the right condition codes
        blt     setn                    /   of the result
        bgt     out                     / adios, amigo!
        bis     $4,rpsw                 / if zero, set the Z bit
        br      out                     / au revoir, mon ami!
setn:   bis     $10,rpsw                / if negative, set the N bit
/*** restore the registers after saving results
out:    mov     rnumbr,snumbr(lr)       / save result
        mov     rpsw,sotpsw(lr)         / save psw
        mov     (lr)+,r4                / restore registers
        mov     (lr)+,r3
        mov     (lr)+,r2
        mov     (lr)+,r1
        mov     (lr)+,r0
        rts     pc                      / do svidanya, tovarishch!
.data