1 /* SImode div/mod functions for the GCC support library for the Renesas RL78 processors.
2 Copyright (C) 2012-2017 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
698 br $!main_loop_done_himode\which
707 ;; initialize bit to 1
711 ; while (den < num && !(den & (1L << BITS_MINUS_1)))
714 ;; see if we can short-circuit a bunch of shifts
717 bnz $shift_den_bit\which
720 bnh $shift_den_bit16\which
726 bc $enter_main_loop\which
728 movw ax, denL ; we re-use this below
731 bh $enter_main_loop\which
734 ; movw ax, denL ; already has it from the cmpw above
750 ;; if we don't need to compute the quotent, we don't need an
751 ;; actual bit *mask*, we just need to keep track of which bit
755 br $shift_den_bit\which
760 ;; if (num >= den) (cmp den > num)
770 ; movw ax, numL ; already has it from the cmpw above
823 enter_main_loop\which:
832 ;; bit is HImode now; check others
833 movw ax, numH ; numerator
835 bnz $bit_high_set\which
836 movw ax, denH ; denominator
838 bz $switch_to_himode\which
848 switch_to_himode\which:
855 bz $main_loop_done_himode\which
857 ;; From here on in, r22, r14, and r18 are all zero
859 main_loop_himode\which:
861 ;; if (num >= den) (cmp den > num)
864 bh $next_loop_himode\which
886 next_loop_himode\which:
908 bnz $main_loop_himode\which
910 main_loop_done_himode\which:
920 pop hl ; bitH - stored in BC
928 END_FUNC __generic_sidivmod\which
931 ;----------------------------------------------------------------------
936 ;----------------------------------------------------------------------
938 START_FUNC ___udivsi3
939 ;; r8 = 4[sp] / 8[sp]
940 call $!__generic_sidiv
945 START_FUNC ___umodsi3
946 ;; r8 = 4[sp] % 8[sp]
947 call $!__generic_simod
951 ;----------------------------------------------------------------------
965 ;----------------------------------------------------------------------
968 ;; r8 = 4[sp] / 8[sp]
976 call $!__generic_sidiv
987 bnc $div_unsigned_den
995 call $!__generic_sidiv
999 bz $div_skip_restore_num
1000 ;; We have to restore the numerator [sp+4]
1005 div_skip_restore_num:
1013 bz $div_skip_restore_den
1014 ;; We have to restore the denominator [sp+8]
1018 div_skip_restore_den:
1023 START_FUNC ___modsi3
1024 ;; r8 = 4[sp] % 8[sp]
1032 call $!__generic_simod
1043 bnc $mod_unsigned_den
1051 call $!__generic_simod
1058 ;; We have to restore [sp+4] as well.
1066 bz $mod_skip_restore_den
1070 mod_skip_restore_den:
1075 ;----------------------------------------------------------------------
1079 #error "Unknown RL78 hardware multiply/divide support"