2016-05-15 Harald Anlauf <anlauf@gmx.de>
[official-gcc.git] / libgcc / config / rl78 / divmodhi.S
blobfd6190322907b0d6545ad88493af287f4d62dc7a
1 /* HImode div/mod functions for the GCC support library for the Renesas RL78 processors.
2    Copyright (C) 2012-2016 Free Software Foundation, Inc.
3    Contributed by Red Hat.
5    This file is part of GCC.
7    GCC is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3, or (at your option)
10    any later version.
12    GCC is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
17    Under Section 7 of GPL version 3, you are granted additional
18    permissions described in the GCC Runtime Library Exception, version
19    3.1, as published by the Free Software Foundation.
21    You should have received a copy of the GNU General Public License and
22    a copy of the GCC Runtime Library Exception along with this program;
23    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24    <http://www.gnu.org/licenses/>.  */
26 #include "vregs.h"
28 #if defined __RL78_MUL_G14__
30 START_FUNC ___divhi3
31         ;; r8 = 4[sp] / 6[sp]
33         ;; Test for a negative denumerator.
34         movw    ax, [sp+6]
35         mov1    cy, a.7
36         movw    de, ax
37         bc      $__div_neg_den
39         ;; Test for a negative numerator.
40         movw    ax, [sp+4]
41         mov1    cy, a.7
42         bc      $__div_neg_num
44         ;; Neither are negative - we can use the unsigned divide instruction.
45 __div_no_convert:       
46         push    psw
47         di
48         divhu
49         pop     psw
50         
51         movw    r8, ax
52         ret
54 __div_neg_den:
55         ;; Negate the denumerator (which is in DE)
56         clrw    ax
57         subw    ax, de
58         movw    de, ax
59         
60         ;; Test for a negative numerator.
61         movw    ax, [sp+4]
62         mov1    cy, a.7
63         ;; If it is not negative then we perform the division and then negate the result.
64         bnc     $__div_then_convert
66         ;; Otherwise we negate the numerator and then go with an unsigned division.
67         movw    bc, ax
68         clrw    ax
69         subw    ax, bc
70         br      $__div_no_convert
72 __div_neg_num:
73         ;; Negate the numerator (which is in AX)
74         ;; We know that the denumerator is positive.
75         movw    bc, ax
76         clrw    ax
77         subw    ax, bc
78         
79 __div_then_convert:
80         push    psw
81         di
82         divhu
83         pop     psw
84         
85         ;; Negate result and transfer into r8
86         movw    bc, ax
87         clrw    ax
88         subw    ax, bc
89         movw    r8, ax
90         ret
92 END_FUNC ___divhi3
94 ;----------------------------------------------------------------------
96 START_FUNC ___modhi3
97         ;; r8 = 4[sp] % 6[sp]
99         ;; Test for a negative denumerator.
100         movw    ax, [sp+6]
101         mov1    cy, a.7
102         movw    de, ax
103         bc      $__mod_neg_den
105         ;; Test for a negative numerator.
106         movw    ax, [sp+4]
107         mov1    cy, a.7
108         bc      $__mod_neg_num
110         ;; Neither are negative - we can use the unsigned divide instruction.
111 __mod_no_convert:       
112         push    psw
113         di
114         divhu
115         pop     psw
117         movw    ax, de
118         movw    r8, ax
119         ret
121 __mod_neg_den:  
122         ;; Negate the denumerator (which is in DE)
123         clrw    ax
124         subw    ax, de
125         movw    de, ax
126         
127         ;; Test for a negative numerator.
128         movw    ax, [sp+4]
129         mov1    cy, a.7
130         ;; If it is not negative then we perform the modulo operation without conversion.
131         bnc     $__mod_no_convert
133         ;; Otherwise we negate the numerator and then go with an unsigned modulo operation.
134         movw    bc, ax
135         clrw    ax
136         subw    ax, bc
137         br      $__mod_then_convert
139 __mod_neg_num:
140         ;; Negate the numerator (which is in AX)
141         ;; We know that the denumerator is positive.
142         movw    bc, ax
143         clrw    ax
144         subw    ax, bc
145         
146 __mod_then_convert:
147         push    psw
148         di
149         divhu
150         pop     psw
152         ;; Negate result and transfer into r8
153         clrw      ax
154         subw      ax, de
155         movw      r8, ax
156         ret
158 END_FUNC ___modhi3
160 ;----------------------------------------------------------------------
162 #elif defined __RL78_MUL_G13__
164         ;; The G13 S2 core does not have a 16 bit divide peripheral.
165         ;; So instead we perform a 32-bit divide and twiddle the inputs
166         ;; as necessary.
168         ;; Hardware registers.  Note - these values match the silicon, not the documentation.
169         MDAL = 0xffff0
170         MDAH = 0xffff2
171         MDBL = 0xffff6
172         MDBH = 0xffff4
173         MDCL = 0xf00e0
174         MDCH = 0xf00e2
175         MDUC = 0xf00e8
177 .macro _Negate src, dest
178         movw    ax, !\src
179         movw    bc, ax
180         clrw    ax
181         subw    ax, bc
182         movw    \dest, ax
183 .endm
184         
185 ;----------------------------------------------------------------------
186         
187 START_FUNC ___divhi3
188         ;; r8 = 4[sp] / 6[sp] (signed division)
190         mov     a, #0xC0        ; Set DIVMODE=1 and MACMODE=1
191         mov     !MDUC, a        ; This preps the peripheral for division without interrupt generation
193         clrw    ax              ; Clear the top 16-bits of the divisor and dividend
194         movw    MDBH, ax
195         movw    MDAH, ax
196         
197         ;; Load and test for a negative denumerator.
198         movw    ax, [sp+6]
199         movw    MDBL, ax
200         mov1    cy, a.7
201         bc      $__div_neg_den
203         ;; Load and test for a negative numerator.
204         movw    ax, [sp+4]
205         mov1    cy, a.7
206         movw    MDAL, ax
207         bc      $__div_neg_num
209         ;; Neither are negative - we can use the unsigned divide hardware.
210 __div_no_convert:       
211         mov     a, #0xC1        ; Set the DIVST bit in MDUC
212         mov     !MDUC, a        ; This starts the division op
214 1:      mov     a, !MDUC        ; Wait 16 clocks or until DIVST is clear
215         bt      a.0, $1b
217         movw    ax, MDAL        ; Read the result
218         movw    r8, ax
219         ret
221 __div_neg_den:
222         ;; Negate the denumerator (which is in MDBL)
223         _Negate MDBL MDBL
225         ;; Load and test for a negative numerator.
226         movw    ax, [sp+4]
227         mov1    cy, a.7
228         movw    MDAL, ax
229         ;; If it is not negative then we perform the division and then negate the result.
230         bnc     $__div_then_convert
232         ;; Otherwise we negate the numerator and then go with a straightforward unsigned division.
233         _Negate MDAL MDAL
234         br      $!__div_no_convert
236 __div_neg_num:
237         ;; Negate the numerator (which is in MDAL)
238         ;; We know that the denumerator is positive.
239         _Negate MDAL MDAL
240         
241 __div_then_convert:
242         mov     a, #0xC1        ; Set the DIVST bit in MDUC
243         mov     !MDUC, a        ; This starts the division op
245 1:      mov     a, !MDUC        ; Wait 16 clocks or until DIVST is clear
246         bt      a.0, $1b
248         ;; Negate result and transfer into r8
249         _Negate MDAL r8
250         ret
252 END_FUNC ___divhi3
254 ;----------------------------------------------------------------------
256 START_FUNC ___modhi3
257         ;; r8 = 4[sp] % 6[sp] (signed modulus)
259         mov     a, #0xC0        ; Set DIVMODE=1 and MACMODE=1
260         mov     !MDUC, a        ; This preps the peripheral for division without interrupt generation
262         clrw    ax              ; Clear the top 16-bits of the divisor and dividend
263         movw    MDBH, ax
264         movw    MDAH, ax
265         
266         ;; Load and test for a negative denumerator.
267         movw    ax, [sp+6]
268         movw    MDBL, ax
269         mov1    cy, a.7
270         bc      $__mod_neg_den
272         ;; Load and test for a negative numerator.
273         movw    ax, [sp+4]
274         mov1    cy, a.7
275         movw    MDAL, ax
276         bc      $__mod_neg_num
278         ;; Neither are negative - we can use the unsigned divide hardware
279 __mod_no_convert:       
280         mov     a, #0xC1        ; Set the DIVST bit in MDUC
281         mov     !MDUC, a        ; This starts the division op
283 1:      mov     a, !MDUC        ; Wait 16 clocks or until DIVST is clear
284         bt      a.0, $1b
286         movw    ax, !MDCL       ; Read the remainder
287         movw    r8, ax
288         ret
290 __mod_neg_den:
291         ;; Negate the denumerator (which is in MDBL)
292         _Negate MDBL MDBL
293         
294         ;; Load and test for a negative numerator.
295         movw    ax, [sp+4]
296         mov1    cy, a.7
297         movw    MDAL, ax
298         ;; If it is not negative then we perform the modulo operation without conversion.
299         bnc     $__mod_no_convert
301         ;; Otherwise we negate the numerator and then go with a modulo followed by negation.
302         _Negate MDAL MDAL
303         br      $!__mod_then_convert
305 __mod_neg_num:
306         ;; Negate the numerator (which is in MDAL)
307         ;; We know that the denumerator is positive.
308         _Negate MDAL MDAL
309         
310 __mod_then_convert:
311         mov     a, #0xC1        ; Set the DIVST bit in MDUC
312         mov     !MDUC, a        ; This starts the division op
314 1:      mov     a, !MDUC        ; Wait 16 clocks or until DIVST is clear
315         bt      a.0, $1b
317         _Negate MDCL r8
318         ret
320 END_FUNC ___modhi3
322 ;----------------------------------------------------------------------
324 START_FUNC ___udivhi3
325         ;; r8 = 4[sp] / 6[sp] (unsigned division)
327         mov     a, #0xC0        ; Set DIVMODE=1 and MACMODE=1
328         mov     !MDUC, a        ; This preps the peripheral for division without interrupt generation
330         movw    ax, [sp+4]      ; Load the divisor
331         movw    MDAL, ax
332         movw    ax, [sp+6]      ; Load the dividend
333         movw    MDBL, ax
334         clrw    ax
335         movw    MDAH, ax
336         movw    MDBH, ax
337         
338         mov     a, #0xC1        ; Set the DIVST bit in MDUC
339         mov     !MDUC, a        ; This starts the division op
341 1:      mov     a, !MDUC        ; Wait 16 clocks or until DIVST is clear
342         bt      a.0, $1b
344         movw    ax, !MDAL       ; Read the remainder
345         movw    r8, ax
346         ret
348 END_FUNC   ___udivhi3
350 ;----------------------------------------------------------------------
352 START_FUNC ___umodhi3
353         ;; r8 = 4[sp] % 6[sp] (unsigned modulus)
355         mov     a, #0xC0        ; Set DIVMODE=1 and MACMODE=1
356         mov     !MDUC, a        ; This preps the peripheral for division without interrupt generation
358         movw    ax, [sp+4]      ; Load the divisor
359         movw    MDAL, ax
360         movw    ax, [sp+6]      ; Load the dividend
361         movw    MDBL, ax
362         clrw    ax
363         movw    MDAH, ax
364         movw    MDBH, ax
365         
366         mov     a, #0xC1        ; Set the DIVST bit in MDUC
367         mov     !MDUC, a        ; This starts the division op
369 1:      mov     a, !MDUC        ; Wait 16 clocks or until DIVST is clear
370         bt      a.0, $1b
372         movw    ax, !MDCL       ; Read the remainder
373         movw    r8, ax
374         ret
375         
376 END_FUNC   ___umodhi3
378 ;----------------------------------------------------------------------
379         
380 #elif defined __RL78_MUL_NONE__
381         
382 .macro MAKE_GENERIC  which,need_result
384         .if \need_result
385         quot = r8
386         num = r10
387         den = r12
388         bit = r14
389         .else
390         num = r8
391         quot = r10
392         den = r12
393         bit = r14
394         .endif
396         quotB0 = quot
397         quotB1 = quot+1
398         
399         numB0 = num
400         numB1 = num+1
401         
402         denB0 = den
403         denB1 = den+1
404         
405         bitB0 = bit
406         bitB1 = bit+1
408 #define bit     bc
409 #define bitB0   c
410 #define bitB1   b
412         START_FUNC __generic_hidivmod\which
414 num_lt_den\which:
415         .if \need_result
416         movw    r8, #0
417         .else
418         movw    ax, [sp+8]
419         movw    r8, ax
420         .endif
421         ret
423         ;; These routines leave DE alone - the signed functions use DE
424         ;; to store sign information that must remain intact
426         .if \need_result
427         .global __generic_hidiv
428 __generic_hidiv:
430         .else
432         .global __generic_himod
433 __generic_himod:
435         .endif
437         ;; (quot,rem) = 8[sp] /% 10[sp]
439         movw    hl, sp
440         movw    ax, [hl+10] ; denH
441         cmpw    ax, [hl+8] ; numH
442         bh      $num_lt_den\which
444         ;; (quot,rem) = 16[sp] /% 20[sp]
446         ;; copy numerator
447         movw    ax, [hl+8]
448         movw    num, ax
450         ;; copy denomonator
451         movw    ax, [hl+10]
452         movw    den, ax
454         movw    ax, den
455         cmpw    ax, #0
456         bnz     $den_not_zero\which
457         .if \need_result
458         movw    quot, #0
459         .else
460         movw    num, #0
461         .endif
462         ret
464 den_not_zero\which:
465         .if \need_result
466         ;; zero out quot
467         movw    quot, #0
468         .endif
470         ;; initialize bit to 1
471         movw    bit, #1
473 ; while (den < num && !(den & (1L << BITS_MINUS_1)))
475 shift_den_bit\which:    
476         movw    ax, den
477         mov1    cy,a.7
478         bc      $enter_main_loop\which
479         cmpw    ax, num
480         bh      $enter_main_loop\which
482         ;; den <<= 1
483 ;       movw    ax, den         ; already has it from the cmpw above
484         shlw    ax, 1
485         movw    den, ax
487         ;; bit <<= 1
488         .if \need_result
489 #ifdef bit
490         shlw    bit, 1
491 #else
492         movw    ax, bit
493         shlw    ax, 1
494         movw    bit, ax
495 #endif
496         .else
497         ;; if we don't need to compute the quotent, we don't need an
498         ;; actual bit *mask*, we just need to keep track of which bit
499         inc     bitB0
500         .endif
502         br      $shift_den_bit\which
504 main_loop\which:
506         ;; if (num >= den) (cmp den > num)
507         movw    ax, den
508         cmpw    ax, num
509         bh      $next_loop\which
511         ;; num -= den
512         movw    ax, num
513         subw    ax, den
514         movw    num, ax
516         .if \need_result
517         ;; res |= bit
518         mov     a, quotB0
519         or      a, bitB0
520         mov     quotB0, a
521         mov     a, quotB1
522         or      a, bitB1
523         mov     quotB1, a
524         .endif
526 next_loop\which:        
528         ;; den >>= 1
529         movw    ax, den
530         shrw    ax, 1
531         movw    den, ax
533         .if \need_result
534         ;; bit >>= 1
535         movw    ax, bit
536         shrw    ax, 1
537         movw    bit, ax
538         .else
539         dec     bitB0
540         .endif
542 enter_main_loop\which:
543         .if \need_result
544         movw    ax, bit
545         cmpw    ax, #0
546         .else
547         cmp0    bitB0
548         .endif
549         bnz     $main_loop\which
551 main_loop_done\which:   
552         ret
553         END_FUNC __generic_hidivmod\which
554 .endm
555 ;----------------------------------------------------------------------
557         MAKE_GENERIC _d 1
558         MAKE_GENERIC _m 0
560 ;----------------------------------------------------------------------
562 START_FUNC ___udivhi3
563         ;; r8 = 4[sp] / 6[sp]
564         call    $!__generic_hidiv
565         ret
566 END_FUNC ___udivhi3
567         
569 START_FUNC ___umodhi3
570         ;; r8 = 4[sp] % 6[sp]
571         call    $!__generic_himod
572         ret
573 END_FUNC ___umodhi3
575 ;----------------------------------------------------------------------
577 .macro NEG_AX
578         movw    hl, ax
579         movw    ax, #0
580         subw    ax, [hl]
581         movw    [hl], ax
582 .endm
584 ;----------------------------------------------------------------------
586 START_FUNC ___divhi3
587         ;; r8 = 4[sp] / 6[sp]
588         movw    de, #0
589         mov     a, [sp+5]
590         mov1    cy, a.7
591         bc      $div_signed_num
592         mov     a, [sp+7]
593         mov1    cy, a.7
594         bc      $div_signed_den
595         call    $!__generic_hidiv
596         ret
597         
598 div_signed_num:
599         ;; neg [sp+4]
600         movw    ax, sp
601         addw    ax, #4
602         NEG_AX
603         mov     d, #1
604         mov     a, [sp+7]
605         mov1    cy, a.7
606         bnc     $div_unsigned_den
607 div_signed_den: 
608         ;; neg [sp+6]
609         movw    ax, sp
610         addw    ax, #6
611         NEG_AX
612         mov     e, #1
613 div_unsigned_den:       
614         call    $!__generic_hidiv
616         mov     a, d
617         cmp0    a
618         bz      $div_skip_restore_num
619         ;;  We have to restore the numerator [sp+4]
620         movw    ax, sp
621         addw    ax, #4
622         NEG_AX
623         mov     a, d
624 div_skip_restore_num:   
625         xor     a, e
626         bz      $div_no_neg
627         movw    ax, #r8
628         NEG_AX
629 div_no_neg:
630         mov     a, e
631         cmp0    a
632         bz      $div_skip_restore_den
633         movw    ax, sp
634         addw    ax, #6
635         NEG_AX
636 div_skip_restore_den:   
637         ret
638 END_FUNC ___divhi3
639         
641 START_FUNC ___modhi3
642         ;; r8 = 4[sp] % 6[sp]
643         movw    de, #0
644         mov     a, [sp+5]
645         mov1    cy, a.7
646         bc      $mod_signed_num
647         mov     a, [sp+7]
648         mov1    cy, a.7
649         bc      $mod_signed_den
650         call    $!__generic_himod
651         ret
652         
653 mod_signed_num:
654         ;; neg [sp+4]
655         movw    ax, sp
656         addw    ax, #4
657         NEG_AX
658         mov     d, #1
659         mov     a, [sp+7]
660         mov1    cy, a.7
661         bnc     $mod_unsigned_den
662 mod_signed_den: 
663         ;; neg [sp+6]
664         movw    ax, sp
665         addw    ax, #6
666         NEG_AX
667 mod_unsigned_den:       
668         call    $!__generic_himod
670         mov     a, d
671         cmp0    a
672         bz      $mod_no_neg
673         movw    ax, #r8
674         NEG_AX
675         ;;  Also restore numerator
676         movw    ax, sp
677         addw    ax, #4
678         NEG_AX
679 mod_no_neg:
680         mov     a, e
681         cmp0    a
682         bz      $mod_skip_restore_den
683         movw    ax, sp
684         addw    ax, #6
685         NEG_AX
686 mod_skip_restore_den:   
687         ret
688 END_FUNC ___modhi3
690 ;----------------------------------------------------------------------
692 #else
694 #error "Unknown RL78 hardware multiply/divide support"
696 #endif