Browse Source

+ ppc implemetnation of fpc_mod/div_qword (from ppc compiler writers guide)

Jonas Maebe 21 years ago
parent
commit
0acd012715
1 changed files with 129 additions and 1 deletions
  1. 129 1
      rtl/powerpc/int64p.inc

+ 129 - 1
rtl/powerpc/int64p.inc

@@ -14,9 +14,137 @@
 
  **********************************************************************}
 
+{$define FPC_SYSTEM_HAS_DIV_QWORD}
+    function fpc_div_qword(n,z : qword) : qword;assembler;[public,alias: 'FPC_DIV_QWORD']; {$ifdef hascompilerproc} compilerproc; {$endif}
+      { from the ppc compiler writers guide }
+      assembler;
+      asm
+        // (R5:R6) = (R5:R6) / (R3:R4) (64b) = (64b / 64b)
+        // quo        dvd         dvs
+        //
+        // Remainder is returned in R3:R4.
+        //
+        // Code comment notation:
+        // msw = most-significant (high-order) word, i.e. bits 0..31
+        // lsw = least-significant (low-order) word, i.e. bits 32..63
+        // LZ = Leading Zeroes
+        // SD = Significant Digits
+        //
+        // R5:R6 = dvd (input dividend); quo (output quotient)
+        // R3:R4 = dvs (input divisor); rem (output remainder)
+        //
+        // R7:R8 = tmp
+        // count the number of leading 0s in the dividend
+        or.     R0,R3,R4 // dvs = 0?
+        cmpwi   cr1,R5,0 // dvd.msw == 0?
+        cntlzw  R0,R5 // R0 = dvd.msw.LZ
+        cntlzw  R9,R6 // R9 = dvd.lsw.LZ
+        bne+    .LNoDivByZero
+        b       FPC_DIVBYZERO
+      .LNoDivByZero:
+        bne     cr1,.Llab1 // if(dvd.msw == 0) dvd.LZ = dvd.msw.LZ
+        addi    R0,R9,32 // dvd.LZ = dvd.lsw.LZ + 32
+      .Llab1:
+        // count the number of leading 0s in the divisor
+        cmpwi   cr0,R3,0 // dvd.msw == 0?
+        cntlzw  R9,R3 // R9 = dvs.msw.LZ
+        cntlzw  R10,R4 // R10 = dvs.lsw.LZ
+        bne     cr0,.Llab2 // if(dvs.msw == 0) dvs.LZ = dvs.msw.LZ
+        addi    R9,R10,32 // dvs.LZ = dvs.lsw.LZ + 32
+      .Llab2:
+        // determine shift amounts to minimize the number of iterations
+        cmpw    cr0,R0,R9 // compare dvd.LZ to dvs.LZ
+        subfic  R10,R0,64 // R10 = dvd.SD
+        bgt     cr0,.Llab9 // if(dvs > dvd) quotient = 0
+        addi    R9,R9,1 // ++dvs.LZ (or --dvs.SD)
+        subfic  R9,R9,64 // R9 = dvs.SD
+        add     R0,R0,R9 // (dvd.LZ + dvs.SD) = left shift of dvd for
+        // initial dvd
+        subf    R9,R9,R10 // (dvd.SD - dvs.SD) = right shift of dvd for
+        // initial tmp
+        mtctr   R9 // number of iterations = dvd.SD - dvs.SD
+        // R7:R8 = R5:R6 >> R9
+        cmpwi   cr0,R9,32 // compare R9 to 32
+        addi    R7,R9,-32
+        blt     cr0,.Llab3 // if(R9 < 32) jump to .Llab3
+        srw     R8,R5,R7 // tmp.lsw = dvd.msw >> (R9 - 32)
+        li      R7,0 // tmp.msw = 0
+        b       .Llab4
+      .Llab3:
+        srw     R8,R6,R9 // R8 = dvd.lsw >> R9
+        subfic  R7,R9,32
+        slw     R7,R5,R7 // R7 = dvd.msw << 32 - R9
+        or      R8,R8,R7 // tmp.lsw = R8 | R7
+        srw     R7,R5,R9 // tmp.msw = dvd.msw >> R9
+      .Llab4:
+        // R5:R6 = R5:R6 << R0
+        cmpwi   cr0,R0,32 // compare R0 to 32
+        addic   R9,R0,-32
+        blt     cr0,.Llab5 // if(R0 < 32) jump to .Llab5
+        slw     R5,R6,R9 // dvd.msw = dvd.lsw << R9
+        li      R6,0 // dvd.lsw = 0
+        b       .Llab6
+      .Llab5:
+        slw     R5,R5,R0 // R5 = dvd.msw << R0
+        subfic  R9,R0,32
+        srw     R9,R6,R9 // R9 = dvd.lsw >> 32 - R0
+        or      R5,R5,R9 // dvd.msw = R5 | R9
+        slw     R6,R6,R0 // dvd.lsw = dvd.lsw << R0
+      .Llab6:
+        // restoring division shift and subtract loop
+        li      R10,-1 // R10 = -1
+        addic   R7,R7,0 // clear carry bit before loop starts
+      .Llab7:
+        // tmp:dvd is considered one large register
+        // each portion is shifted left 1 bit by adding it to itself
+        // adde sums the carry from the previous and creates a new carry
+        adde    R6,R6,R6 // shift dvd.lsw left 1 bit
+        adde    R5,R5,R5 // shift dvd.msw to left 1 bit
+        adde    R8,R8,R8 // shift tmp.lsw to left 1 bit
+        adde    R7,R7,R7 // shift tmp.msw to left 1 bit
+        subfc   R0,R4,R8 // tmp.lsw - dvs.lsw
+        subfe.  R9,R3,R7 // tmp.msw - dvs.msw
+        blt     cr0,.Llab8 // if(result < 0) clear carry bit
+        mr      R8,R0 // move lsw
+        mr      R7,R9 // move msw
+        addic   R0,R10,1 // set carry bit
+      .Llab8:
+        bdnz    .Llab7
+        // write quotient and remainder
+        adde    R4,R6,R6 // quo.lsw (lsb = CA)
+        adde    R3,R5,R5 // quo.msw (lsb from lsw)
+        mr      R6,R8 // rem.lsw
+        mr      R5,R7 // rem.msw
+        b       .Lqworddivdone // return
+      .Llab9:
+        // Quotient is 0 (dvs > dvd)
+        li     R4,0 // dvd.lsw = 0
+        li     R3,0 // dvd.msw = 0
+      .Lqworddivdone:
+      end;
+
+
+{$define FPC_SYSTEM_HAS_MOD_QWORD}
+    function fpc_mod_qword(n,z : qword) : qword;assembler;[public,alias: 'FPC_MOD_QWORD']; {$ifdef hascompilerproc} compilerproc; {$endif}
+      assembler;
+      var
+        oldlr: pointer;
+      asm
+        mflr r0
+        stw  r0,oldlr
+        bl   FPC_DIV_QWORD
+        lwz  r0,oldlr
+        mtlr r0
+        mr   R3,R5
+        mr   R4,R6
+      end;
+
 {
   $Log$
-  Revision 1.1  2003-09-14 11:34:13  peter
+  Revision 1.2  2004-01-12 18:03:30  jonas
+    + ppc implemetnation of fpc_mod/div_qword (from ppc compiler writers guide)
+
+  Revision 1.1  2003/09/14 11:34:13  peter
     * moved int64 asm code to int64p.inc
     * save ebx,esi