1 /* SImode div/mod functions for the GCC support library for the Renesas RL78 processors.
2 Copyright (C) 2012-2015 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 it
8 under the terms of the GNU General Public License as published
9 by the Free Software Foundation; either version 3, or (at your
10 option) any later version.
12 GCC is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
15 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/>. */
28 #if defined __RL78_MUL_G14__
31 ;; r8,r10 = 4[sp],6[sp] / 8[sp],10[sp]
33 ;; Load and test for a negative denumerator.
41 ;; Load and test for a negative numerator.
48 ;; Neither are negative - we can use the unsigned divide instruction.
61 ;; Negate the denumerator (which is in HLDE)
71 ;; Load and test for a negative numerator.
76 ;; If it is not negative then we perform the division and then negate the result.
77 bnc $__div_then_convert
79 ;; Otherwise we negate the numerator and then go with a straightforward unsigned division.
80 ;; The negation is complicated because AX, BC, DE and HL are already in use.
81 ;; ax: numL bc: numH r8: r10:
83 ;; ax: numH bc: numL r8: r10:
85 ;; ax: bc: numL r8: numH r10:
87 ;; ax: 0 bc: numL r8: numH r10:
89 ;; ax: -numL bc: r8: numH r10:
91 ;; ax: bc: r8: numH r10: -numL
93 ;; ax: numH bc: r8: r10: -numL
95 ;; ax: bc: numH r8: r10: -numL
97 ;; ax: 0 bc: numH r8: r10: -numL
100 ;; ax: -1 bc: numH r8: r10: -numL
102 ;; ax: -numH bc: r8: r10: -numL
104 ;; ax: bc: -numH r8: r10: -numL
106 ;; ax: -numL bc: -numH r8: r10:
107 br $!__div_no_convert
110 ;; Negate the numerator (which is in BCAX)
111 ;; We know that the denumerator is positive.
112 ;; Note - we temporarily overwrite DE. We know that we can safely load it again off the stack again.
132 ;; Negate result (in BCAX) and transfer into r8,r10
146 ;----------------------------------------------------------------------
148 START_FUNC ___udivsi3
149 ;; r8,r10 = 4[sp],6[sp] / 8[sp],10[sp]
150 ;; Used when compiling with -Os specified.
159 push psw ; Save the current interrupt status
160 di ; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E
161 divwu ; bcax = bcax / hlde
162 pop psw ; Restore saved interrupt status
170 ;----------------------------------------------------------------------
173 ;; r8,r10 = 4[sp],6[sp] % 8[sp],10[sp]
175 ;; Load and test for a negative denumerator.
183 ;; Load and test for a negative numerator.
190 ;; Neither are negative - we can use the unsigned divide instruction.
204 ;; Negate the denumerator (which is in HLDE)
214 ;; Load and test for a negative numerator.
219 ;; If it is not negative then we perform the modulo operation without conversion
220 bnc $__mod_no_convert
222 ;; Otherwise we negate the numerator and then go with a modulo followed by negation.
223 ;; The negation is complicated because AX, BC, DE and HL are already in use.
237 br $!__mod_then_convert
240 ;; Negate the numerator (which is in BCAX)
241 ;; We know that the denumerator is positive.
242 ;; Note - we temporarily overwrite DE. We know that we can safely load it again off the stack again.
262 ;; Negate result (in HLDE) and transfer into r8,r10
275 ;----------------------------------------------------------------------
277 START_FUNC ___umodsi3
278 ;; r8,r10 = 4[sp],6[sp] % 8[sp],10[sp]
279 ;; Used when compiling with -Os specified.
288 push psw ; Save the current interrupt status
289 di ; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E
290 divwu ; hlde = bcax %% hlde
291 pop psw ; Restore saved interrupt status
300 ;----------------------------------------------------------------------
302 #elif defined __RL78_MUL_G13__
304 ;----------------------------------------------------------------------
306 ;; Hardware registers. Note - these values match the silicon, not the documentation.
315 .macro _Negate low, high
330 ;----------------------------------------------------------------------
333 ;; r8,r10 = 4[sp],6[sp] / 8[sp],10[sp]
335 mov a, #0xC0 ; Set DIVMODE=1 and MACMODE=1
336 mov !MDUC, a ; This preps the peripheral for division without interrupt generation
338 ;; Load and test for a negative denumerator.
346 ;; Load and test for a negative numerator.
354 ;; Neither are negative - we can use the unsigned divide hardware.
356 mov a, #0xC1 ; Set the DIVST bit in MDUC
357 mov !MDUC, a ; This starts the division op
359 1: mov a, !MDUC ; Wait 16 clocks or until DIVST is clear
362 movw ax, MDAL ; Read the result
369 ;; Negate the denumerator (which is in MDBL/MDBH)
372 ;; Load and test for a negative numerator.
378 ;; If it is not negative then we perform the division and then negate the result.
379 bnc $__div_then_convert
381 ;; Otherwise we negate the numerator and then go with a straightforward unsigned division.
383 br $!__div_no_convert
386 ;; Negate the numerator (which is in MDAL/MDAH)
387 ;; We know that the denumerator is positive.
391 mov a, #0xC1 ; Set the DIVST bit in MDUC
392 mov !MDUC, a ; This starts the division op
394 1: mov a, !MDUC ; Wait 16 clocks or until DIVST is clear
397 ;; Negate result and transfer into r8,r10
398 _Negate MDAL MDAH ; FIXME: This could be coded more efficiently.
407 ;----------------------------------------------------------------------
410 ;; r8,r10 = 4[sp],6[sp] % 8[sp],10[sp]
412 mov a, #0xC0 ; Set DIVMODE=1 and MACMODE=1
413 mov !MDUC, a ; This preps the peripheral for division without interrupt generation
415 ;; Load and test for a negative denumerator.
423 ;; Load and test for a negative numerator.
431 ;; Neither are negative - we can use the unsigned divide hardware
433 mov a, #0xC1 ; Set the DIVST bit in MDUC
434 mov !MDUC, a ; This starts the division op
436 1: mov a, !MDUC ; Wait 16 clocks or until DIVST is clear
439 movw ax, !MDCL ; Read the remainder
446 ;; Negate the denumerator (which is in MDBL/MDBH)
449 ;; Load and test for a negative numerator.
455 ;; If it is not negative then we perform the modulo operation without conversion
456 bnc $__mod_no_convert
458 ;; Otherwise we negate the numerator and then go with a modulo followed by negation.
460 br $!__mod_then_convert
463 ;; Negate the numerator (which is in MDAL/MDAH)
464 ;; We know that the denumerator is positive.
468 mov a, #0xC1 ; Set the DIVST bit in MDUC
469 mov !MDUC, a ; This starts the division op
471 1: mov a, !MDUC ; Wait 16 clocks or until DIVST is clear
490 ;----------------------------------------------------------------------
492 START_FUNC ___udivsi3
493 ;; r8,r10 = 4[sp],6[sp] / 8[sp],10[sp]
494 ;; Used when compilng with -Os specified.
496 mov a, #0xC0 ; Set DIVMODE=1 and MACMODE=1
497 mov !MDUC, a ; This preps the peripheral for division without interrupt generation
499 movw ax, [sp+4] ; Load the divisor
503 movw ax, [sp+8] ; Load the dividend
508 mov a, #0xC1 ; Set the DIVST bit in MDUC
509 mov !MDUC, a ; This starts the division op
511 1: mov a, !MDUC ; Wait 16 clocks or until DIVST is clear
514 movw ax, !MDAL ; Read the result
522 ;----------------------------------------------------------------------
524 START_FUNC ___umodsi3
525 ;; r8,r10 = 4[sp],6[sp] % 8[sp],10[sp]
526 ;; Used when compilng with -Os specified.
527 ;; Note - hardware address match the silicon, not the documentation
529 mov a, #0xC0 ; Set DIVMODE=1 and MACMODE=1
530 mov !MDUC, a ; This preps the peripheral for division without interrupt generation
532 movw ax, [sp+4] ; Load the divisor
536 movw ax, [sp+8] ; Load the dividend
541 mov a, #0xC1 ; Set the DIVST bit in MDUC
542 mov !MDUC, a ; This starts the division op
544 1: mov a, !MDUC ; Wait 16 clocks or until DIVST is clear
547 movw ax, !MDCL ; Read the remainder
555 ;----------------------------------------------------------------------
557 #elif defined __RL78_MUL_NONE__
559 .macro MAKE_GENERIC which,need_result
601 ;----------------------------------------------------------------------
603 START_FUNC __generic_sidivmod\which
617 shift_den_bit16\which:
630 br $shift_den_bit\which
632 ;; These routines leave DE alone - the signed functions use DE
633 ;; to store sign information that must remain intact
636 .global __generic_sidiv
641 .global __generic_simod
646 ;; (quot,rem) = 8[sp] /% 12[sp]
649 movw ax, [hl+14] ; denH
650 cmpw ax, [hl+10] ; numH
651 movw ax, [hl+12] ; denL
653 cmpw ax, [hl+8] ; numL
668 push hl ; bitH - stored in BC
672 ;; (quot,rem) = 16[sp] /% 20[sp]
690 bnz $den_not_zero\which
702 ;; initialize bit to 1
706 ; while (den < num && !(den & (1L << BITS_MINUS_1)))
709 ;; see if we can short-circuit a bunch of shifts
712 bnz $shift_den_bit\which
715 bnh $shift_den_bit16\which
721 bc $enter_main_loop\which
723 movw ax, denL ; we re-use this below
726 bh $enter_main_loop\which
729 ; movw ax, denL ; already has it from the cmpw above
745 ;; if we don't need to compute the quotent, we don't need an
746 ;; actual bit *mask*, we just need to keep track of which bit
750 br $shift_den_bit\which
755 ;; if (num >= den) (cmp den > num)
765 ; movw ax, numL ; already has it from the cmpw above
818 enter_main_loop\which:
827 ;; bit is HImode now; check others
828 movw ax, numH ; numerator
830 bnz $bit_high_set\which
831 movw ax, denH ; denominator
833 bz $switch_to_himode\which
843 switch_to_himode\which:
850 bz $main_loop_done_himode\which
852 ;; From here on in, r22, r14, and r18 are all zero
854 main_loop_himode\which:
856 ;; if (num >= den) (cmp den > num)
859 bh $next_loop_himode\which
881 next_loop_himode\which:
903 bnz $main_loop_himode\which
905 main_loop_done_himode\which:
915 pop hl ; bitH - stored in BC
923 END_FUNC __generic_sidivmod\which
926 ;----------------------------------------------------------------------
931 ;----------------------------------------------------------------------
933 START_FUNC ___udivsi3
934 ;; r8 = 4[sp] / 8[sp]
935 call $!__generic_sidiv
940 START_FUNC ___umodsi3
941 ;; r8 = 4[sp] % 8[sp]
942 call $!__generic_simod
946 ;----------------------------------------------------------------------
960 ;----------------------------------------------------------------------
963 ;; r8 = 4[sp] / 8[sp]
971 call $!__generic_sidiv
982 bnc $div_unsigned_den
990 call $!__generic_sidiv
994 bz $div_skip_restore_num
995 ;; We have to restore the numerator [sp+4]
1000 div_skip_restore_num:
1008 bz $div_skip_restore_den
1009 ;; We have to restore the denominator [sp+8]
1013 div_skip_restore_den:
1018 START_FUNC ___modsi3
1019 ;; r8 = 4[sp] % 8[sp]
1027 call $!__generic_simod
1038 bnc $mod_unsigned_den
1046 call $!__generic_simod
1053 ;; We have to restore [sp+4] as well.
1061 bz $mod_skip_restore_den
1065 mod_skip_restore_den:
1070 ;----------------------------------------------------------------------
1074 #error "Unknown RL78 hardware multiply/divide support"