/* SImode div/mod functions for the GCC support library for the Renesas RL78 processors. Copyright (C) 2012-2020 Free Software Foundation, Inc. Contributed by Red Hat. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Under Section 7 of GPL version 3, you are granted additional permissions described in the GCC Runtime Library Exception, version 3.1, as published by the Free Software Foundation. You should have received a copy of the GNU General Public License and a copy of the GCC Runtime Library Exception along with this program; see the files COPYING3 and COPYING.RUNTIME respectively. If not, see . */ #include "vregs.h" #if defined __RL78_MUL_G14__ START_FUNC ___divsi3 ;; r8,r10 = 4[sp],6[sp] / 8[sp],10[sp] ;; Load and test for a negative denumerator. movw ax, [sp+8] movw de, ax movw ax, [sp+10] mov1 cy, a.7 movw hl, ax bc $__div_neg_den ;; Load and test for a negative numerator. movw ax, [sp+6] mov1 cy, a.7 movw bc, ax movw ax, [sp+4] bc $__div_neg_num ;; Neither are negative - we can use the unsigned divide instruction. __div_no_convert: push psw di divwu pop psw movw r8, ax movw ax, bc movw r10, ax ret __div_neg_den: ;; Negate the denumerator (which is in HLDE) clrw ax subw ax, de movw de, ax clrw ax sknc decw ax subw ax, hl movw hl, ax ;; Load and test for a negative numerator. movw ax, [sp+6] mov1 cy, a.7 movw bc, ax movw ax, [sp+4] ;; If it is not negative then we perform the division and then negate the result. bnc $__div_then_convert ;; Otherwise we negate the numerator and then go with a straightforward unsigned division. ;; The negation is complicated because AX, BC, DE and HL are already in use. ;; ax: numL bc: numH r8: r10: xchw ax, bc ;; ax: numH bc: numL r8: r10: movw r8, ax ;; ax: bc: numL r8: numH r10: clrw ax ;; ax: 0 bc: numL r8: numH r10: subw ax, bc ;; ax: -numL bc: r8: numH r10: movw r10, ax ;; ax: bc: r8: numH r10: -numL movw ax, r8 ;; ax: numH bc: r8: r10: -numL movw bc, ax ;; ax: bc: numH r8: r10: -numL clrw ax ;; ax: 0 bc: numH r8: r10: -numL sknc decw ax ;; ax: -1 bc: numH r8: r10: -numL subw ax, bc ;; ax: -numH bc: r8: r10: -numL movw bc, ax ;; ax: bc: -numH r8: r10: -numL movw ax, r10 ;; ax: -numL bc: -numH r8: r10: br $!__div_no_convert __div_neg_num: ;; Negate the numerator (which is in BCAX) ;; We know that the denumerator is positive. ;; Note - we temporarily overwrite DE. We know that we can safely load it again off the stack again. movw de, ax clrw ax subw ax, de movw de, ax clrw ax sknc decw ax subw ax, bc movw bc, ax movw ax, [sp+8] xchw ax, de __div_then_convert: push psw di divwu pop psw ;; Negate result (in BCAX) and transfer into r8,r10 movw de, ax clrw ax subw ax, de movw r8, ax clrw ax sknc decw ax subw ax, bc movw r10, ax ret END_FUNC ___divsi3 ;---------------------------------------------------------------------- START_FUNC ___udivsi3 ;; r8,r10 = 4[sp],6[sp] / 8[sp],10[sp] ;; Used when compiling with -Os specified. movw ax, [sp+10] movw hl, ax movw ax, [sp+8] movw de, ax movw ax, [sp+6] movw bc, ax movw ax, [sp+4] push psw ; Save the current interrupt status di ; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E divwu ; bcax = bcax / hlde pop psw ; Restore saved interrupt status movw r8, ax movw ax, bc movw r10, ax ret END_FUNC ___udivsi3 ;---------------------------------------------------------------------- START_FUNC ___modsi3 ;; r8,r10 = 4[sp],6[sp] % 8[sp],10[sp] ;; Load and test for a negative denumerator. movw ax, [sp+8] movw de, ax movw ax, [sp+10] mov1 cy, a.7 movw hl, ax bc $__mod_neg_den ;; Load and test for a negative numerator. movw ax, [sp+6] mov1 cy, a.7 movw bc, ax movw ax, [sp+4] bc $__mod_neg_num ;; Neither are negative - we can use the unsigned divide instruction. __mod_no_convert: push psw di divwu pop psw movw ax, de movw r8, ax movw ax, hl movw r10, ax ret __mod_neg_den: ;; Negate the denumerator (which is in HLDE) clrw ax subw ax, de movw de, ax clrw ax sknc decw ax subw ax, hl movw hl, ax ;; Load and test for a negative numerator. movw ax, [sp+6] mov1 cy, a.7 movw bc, ax movw ax, [sp+4] ;; If it is not negative then we perform the modulo operation without conversion bnc $__mod_no_convert ;; Otherwise we negate the numerator and then go with a modulo followed by negation. ;; The negation is complicated because AX, BC, DE and HL are already in use. xchw ax, bc movw r8, ax clrw ax subw ax, bc movw r10, ax movw ax, r8 movw bc, ax clrw ax sknc decw ax subw ax, bc movw bc, ax movw ax, r10 br $!__mod_then_convert __mod_neg_num: ;; Negate the numerator (which is in BCAX) ;; We know that the denumerator is positive. ;; Note - we temporarily overwrite DE. We know that we can safely load it again off the stack again. movw de, ax clrw ax subw ax, de movw de, ax clrw ax sknc decw ax subw ax, bc movw bc, ax movw ax, [sp+8] xchw ax, de __mod_then_convert: push psw di divwu pop psw ;; Negate result (in HLDE) and transfer into r8,r10 clrw ax subw ax, de movw r8, ax clrw ax sknc decw ax subw ax, hl movw r10, ax ret END_FUNC ___modsi3 ;---------------------------------------------------------------------- START_FUNC ___umodsi3 ;; r8,r10 = 4[sp],6[sp] % 8[sp],10[sp] ;; Used when compiling with -Os specified. movw ax, [sp+10] movw hl, ax movw ax, [sp+8] movw de, ax movw ax, [sp+6] movw bc, ax movw ax, [sp+4] push psw ; Save the current interrupt status di ; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E divwu ; hlde = bcax %% hlde pop psw ; Restore saved interrupt status movw ax, de movw r8, ax movw ax, hl movw r10, ax ret END_FUNC ___umodsi3 ;---------------------------------------------------------------------- #elif defined __RL78_MUL_G13__ ;---------------------------------------------------------------------- ;; Hardware registers. Note - these values match the silicon, not the documentation. MDAL = 0xffff0 MDAH = 0xffff2 MDBL = 0xffff6 MDBH = 0xffff4 MDCL = 0xf00e0 MDCH = 0xf00e2 MDUC = 0xf00e8 .macro _Negate low, high movw ax, \low movw bc, ax clrw ax subw ax, bc movw \low, ax movw ax, \high movw bc, ax clrw ax sknc decw ax subw ax, bc movw \high, ax .endm ;---------------------------------------------------------------------- START_FUNC ___divsi3 ;; r8,r10 = 4[sp],6[sp] / 8[sp],10[sp] mov a, #0xC0 ; Set DIVMODE=1 and MACMODE=1 mov !MDUC, a ; This preps the peripheral for division without interrupt generation ;; Load and test for a negative denumerator. movw ax, [sp+8] movw MDBL, ax movw ax, [sp+10] mov1 cy, a.7 movw MDBH, ax bc $__div_neg_den ;; Load and test for a negative numerator. movw ax, [sp+6] mov1 cy, a.7 movw MDAH, ax movw ax, [sp+4] movw MDAL, ax bc $__div_neg_num ;; Neither are negative - we can use the unsigned divide hardware. __div_no_convert: mov a, #0xC1 ; Set the DIVST bit in MDUC mov !MDUC, a ; This starts the division op 1: mov a, !MDUC ; Wait 16 clocks or until DIVST is clear bt a.0, $1b movw ax, MDAL ; Read the result movw r8, ax movw ax, MDAH movw r10, ax ret __div_neg_den: ;; Negate the denumerator (which is in MDBL/MDBH) _Negate MDBL MDBH ;; Load and test for a negative numerator. movw ax, [sp+6] mov1 cy, a.7 movw MDAH, ax movw ax, [sp+4] movw MDAL, ax ;; If it is not negative then we perform the division and then negate the result. bnc $__div_then_convert ;; Otherwise we negate the numerator and then go with a straightforward unsigned division. _Negate MDAL MDAH br $!__div_no_convert __div_neg_num: ;; Negate the numerator (which is in MDAL/MDAH) ;; We know that the denumerator is positive. _Negate MDAL MDAH __div_then_convert: mov a, #0xC1 ; Set the DIVST bit in MDUC mov !MDUC, a ; This starts the division op 1: mov a, !MDUC ; Wait 16 clocks or until DIVST is clear bt a.0, $1b ;; Negate result and transfer into r8,r10 _Negate MDAL MDAH ; FIXME: This could be coded more efficiently. movw r10, ax movw ax, MDAL movw r8, ax ret END_FUNC ___divsi3 ;---------------------------------------------------------------------- START_FUNC ___modsi3 ;; r8,r10 = 4[sp],6[sp] % 8[sp],10[sp] mov a, #0xC0 ; Set DIVMODE=1 and MACMODE=1 mov !MDUC, a ; This preps the peripheral for division without interrupt generation ;; Load and test for a negative denumerator. movw ax, [sp+8] movw MDBL, ax movw ax, [sp+10] mov1 cy, a.7 movw MDBH, ax bc $__mod_neg_den ;; Load and test for a negative numerator. movw ax, [sp+6] mov1 cy, a.7 movw MDAH, ax movw ax, [sp+4] movw MDAL, ax bc $__mod_neg_num ;; Neither are negative - we can use the unsigned divide hardware __mod_no_convert: mov a, #0xC1 ; Set the DIVST bit in MDUC mov !MDUC, a ; This starts the division op 1: mov a, !MDUC ; Wait 16 clocks or until DIVST is clear bt a.0, $1b movw ax, !MDCL ; Read the remainder movw r8, ax movw ax, !MDCH movw r10, ax ret __mod_neg_den: ;; Negate the denumerator (which is in MDBL/MDBH) _Negate MDBL MDBH ;; Load and test for a negative numerator. movw ax, [sp+6] mov1 cy, a.7 movw MDAH, ax movw ax, [sp+4] movw MDAL, ax ;; If it is not negative then we perform the modulo operation without conversion bnc $__mod_no_convert ;; Otherwise we negate the numerator and then go with a modulo followed by negation. _Negate MDAL MDAH br $!__mod_then_convert __mod_neg_num: ;; Negate the numerator (which is in MDAL/MDAH) ;; We know that the denumerator is positive. _Negate MDAL MDAH __mod_then_convert: mov a, #0xC1 ; Set the DIVST bit in MDUC mov !MDUC, a ; This starts the division op 1: mov a, !MDUC ; Wait 16 clocks or until DIVST is clear bt a.0, $1b movw ax, !MDCL movw bc, ax clrw ax subw ax, bc movw r8, ax movw ax, !MDCH movw bc, ax clrw ax sknc decw ax subw ax, bc movw r10, ax ret END_FUNC ___modsi3 ;---------------------------------------------------------------------- START_FUNC ___udivsi3 ;; r8,r10 = 4[sp],6[sp] / 8[sp],10[sp] ;; Used when compilng with -Os specified. mov a, #0xC0 ; Set DIVMODE=1 and MACMODE=1 mov !MDUC, a ; This preps the peripheral for division without interrupt generation movw ax, [sp+4] ; Load the divisor movw MDAL, ax movw ax, [sp+6] movw MDAH, ax movw ax, [sp+8] ; Load the dividend movw MDBL, ax movw ax, [sp+10] movw MDBH, ax mov a, #0xC1 ; Set the DIVST bit in MDUC mov !MDUC, a ; This starts the division op 1: mov a, !MDUC ; Wait 16 clocks or until DIVST is clear bt a.0, $1b movw ax, !MDAL ; Read the result movw r8, ax movw ax, !MDAH movw r10, ax ret END_FUNC ___udivsi3 ;---------------------------------------------------------------------- START_FUNC ___umodsi3 ;; r8,r10 = 4[sp],6[sp] % 8[sp],10[sp] ;; Used when compilng with -Os specified. ;; Note - hardware address match the silicon, not the documentation mov a, #0xC0 ; Set DIVMODE=1 and MACMODE=1 mov !MDUC, a ; This preps the peripheral for division without interrupt generation movw ax, [sp+4] ; Load the divisor movw MDAL, ax movw ax, [sp+6] movw MDAH, ax movw ax, [sp+8] ; Load the dividend movw MDBL, ax movw ax, [sp+10] movw MDBH, ax mov a, #0xC1 ; Set the DIVST bit in MDUC mov !MDUC, a ; This starts the division op 1: mov a, !MDUC ; Wait 16 clocks or until DIVST is clear bt a.0, $1b movw ax, !MDCL ; Read the remainder movw r8, ax movw ax, !MDCH movw r10, ax ret END_FUNC ___umodsi3 ;---------------------------------------------------------------------- #elif defined __RL78_MUL_NONE__ .macro MAKE_GENERIC which,need_result .if \need_result quot = r8 num = r12 den = r16 bit = r20 .else num = r8 quot = r12 den = r16 bit = r20 .endif quotH = quot+2 quotL = quot quotB0 = quot quotB1 = quot+1 quotB2 = quot+2 quotB3 = quot+3 numH = num+2 numL = num numB0 = num numB1 = num+1 numB2 = num+2 numB3 = num+3 #define denH bc denL = den denB0 = den denB1 = den+1 #define denB2 c #define denB3 b bitH = bit+2 bitL = bit bitB0 = bit bitB1 = bit+1 bitB2 = bit+2 bitB3 = bit+3 ;---------------------------------------------------------------------- START_FUNC __generic_sidivmod\which num_lt_den\which: .if \need_result movw r8, #0 movw r10, #0 .else movw ax, [sp+8] movw r8, ax movw ax, [sp+10] movw r10, ax .endif ret shift_den_bit16\which: movw ax, denL movw denH, ax movw denL, #0 .if \need_result movw ax, bitL movw bitH, ax movw bitL, #0 .else mov a, bit add a, #16 mov bit, a .endif br $shift_den_bit\which ;; These routines leave DE alone - the signed functions use DE ;; to store sign information that must remain intact .if \need_result .global __generic_sidiv __generic_sidiv: .else .global __generic_simod __generic_simod: .endif ;; (quot,rem) = 8[sp] /% 12[sp] movw hl, sp movw ax, [hl+14] ; denH cmpw ax, [hl+10] ; numH movw ax, [hl+12] ; denL sknz cmpw ax, [hl+8] ; numL bh $num_lt_den\which #ifdef __RL78_G10__ movw ax, denL push ax movw ax, bitL push ax movw ax, bitH push ax #else sel rb2 push ax ; denL ; push bc ; denH push de ; bitL push hl ; bitH - stored in BC sel rb0 #endif ;; (quot,rem) = 16[sp] /% 20[sp] ;; copy numerator movw ax, [hl+8] movw numL, ax movw ax, [hl+10] movw numH, ax ;; copy denomonator movw ax, [hl+12] movw denL, ax movw ax, [hl+14] movw denH, ax movw ax, denL or a, denB2 or a, denB3 ; not x cmpw ax, #0 bnz $den_not_zero\which .if \need_result movw quotL, #0 movw quotH, #0 .else movw numL, #0 movw numH, #0 .endif br $!main_loop_done_himode\which den_not_zero\which: .if \need_result ;; zero out quot movw quotL, #0 movw quotH, #0 .endif ;; initialize bit to 1 movw bitL, #1 movw bitH, #0 ; while (den < num && !(den & (1L << BITS_MINUS_1))) .if 1 ;; see if we can short-circuit a bunch of shifts movw ax, denH cmpw ax, #0 bnz $shift_den_bit\which movw ax, denL cmpw ax, numH bnh $shift_den_bit16\which .endif shift_den_bit\which: movw ax, denH mov1 cy,a.7 bc $enter_main_loop\which cmpw ax, numH movw ax, denL ; we re-use this below sknz cmpw ax, numL bh $enter_main_loop\which ;; den <<= 1 ; movw ax, denL ; already has it from the cmpw above shlw ax, 1 movw denL, ax ; movw ax, denH rolwc denH, 1 ; movw denH, ax ;; bit <<= 1 .if \need_result movw ax, bitL shlw ax, 1 movw bitL, ax movw ax, bitH rolwc ax, 1 movw bitH, ax .else ;; if we don't need to compute the quotent, we don't need an ;; actual bit *mask*, we just need to keep track of which bit inc bitB0 .endif br $shift_den_bit\which ;; while (bit) main_loop\which: ;; if (num >= den) (cmp den > num) movw ax, numH cmpw ax, denH movw ax, numL sknz cmpw ax, denL skz bnh $next_loop\which ;; num -= den ; movw ax, numL ; already has it from the cmpw above subw ax, denL movw numL, ax movw ax, numH sknc decw ax subw ax, denH movw numH, ax .if \need_result ;; res |= bit mov a, quotB0 or a, bitB0 mov quotB0, a mov a, quotB1 or a, bitB1 mov quotB1, a mov a, quotB2 or a, bitB2 mov quotB2, a mov a, quotB3 or a, bitB3 mov quotB3, a .endif next_loop\which: ;; den >>= 1 movw ax, denH shrw ax, 1 movw denH, ax mov a, denB1 rorc a, 1 mov denB1, a mov a, denB0 rorc a, 1 mov denB0, a ;; bit >>= 1 .if \need_result movw ax, bitH shrw ax, 1 movw bitH, ax mov a, bitB1 rorc a, 1 mov bitB1, a mov a, bitB0 rorc a, 1 mov bitB0, a .else dec bitB0 .endif enter_main_loop\which: .if \need_result movw ax, bitH cmpw ax, #0 bnz $main_loop\which .else cmp bitB0, #15 bh $main_loop\which .endif ;; bit is HImode now; check others movw ax, numH ; numerator cmpw ax, #0 bnz $bit_high_set\which movw ax, denH ; denominator cmpw ax, #0 bz $switch_to_himode\which bit_high_set\which: .if \need_result movw ax, bitL cmpw ax, #0 .else cmp0 bitB0 .endif bnz $main_loop\which switch_to_himode\which: .if \need_result movw ax, bitL cmpw ax, #0 .else cmp0 bitB0 .endif bz $main_loop_done_himode\which ;; From here on in, r22, r14, and r18 are all zero ;; while (bit) main_loop_himode\which: ;; if (num >= den) (cmp den > num) movw ax, denL cmpw ax, numL bh $next_loop_himode\which ;; num -= den movw ax, numL subw ax, denL movw numL, ax movw ax, numH sknc decw ax subw ax, denH movw numH, ax .if \need_result ;; res |= bit mov a, quotB0 or a, bitB0 mov quotB0, a mov a, quotB1 or a, bitB1 mov quotB1, a .endif next_loop_himode\which: ;; den >>= 1 movw ax, denL shrw ax, 1 movw denL, ax .if \need_result ;; bit >>= 1 movw ax, bitL shrw ax, 1 movw bitL, ax .else dec bitB0 .endif .if \need_result movw ax, bitL cmpw ax, #0 .else cmp0 bitB0 .endif bnz $main_loop_himode\which main_loop_done_himode\which: #ifdef __RL78_G10__ pop ax movw bitH, ax pop ax movw bitL, ax pop ax movw denL, ax #else sel rb2 pop hl ; bitH - stored in BC pop de ; bitL ; pop bc ; denH pop ax ; denL sel rb0 #endif ret END_FUNC __generic_sidivmod\which .endm ;---------------------------------------------------------------------- MAKE_GENERIC _d 1 MAKE_GENERIC _m 0 ;---------------------------------------------------------------------- START_FUNC ___udivsi3 ;; r8 = 4[sp] / 8[sp] call $!__generic_sidiv ret END_FUNC ___udivsi3 START_FUNC ___umodsi3 ;; r8 = 4[sp] % 8[sp] call $!__generic_simod ret END_FUNC ___umodsi3 ;---------------------------------------------------------------------- .macro NEG_AX movw hl, ax movw ax, #0 subw ax, [hl] movw [hl], ax movw ax, #0 sknc decw ax subw ax, [hl+2] movw [hl+2], ax .endm ;---------------------------------------------------------------------- START_FUNC ___divsi3 ;; r8 = 4[sp] / 8[sp] movw de, #0 mov a, [sp+7] mov1 cy, a.7 bc $div_signed_num mov a, [sp+11] mov1 cy, a.7 bc $div_signed_den call $!__generic_sidiv ret div_signed_num: ;; neg [sp+4] movw ax, sp addw ax, #4 NEG_AX mov d, #1 mov a, [sp+11] mov1 cy, a.7 bnc $div_unsigned_den div_signed_den: ;; neg [sp+8] movw ax, sp addw ax, #8 NEG_AX mov e, #1 div_unsigned_den: call $!__generic_sidiv mov a, d cmp0 a bz $div_skip_restore_num ;; We have to restore the numerator [sp+4] movw ax, sp addw ax, #4 NEG_AX mov a, d div_skip_restore_num: xor a, e bz $div_no_neg movw ax, #r8 NEG_AX div_no_neg: mov a, e cmp0 a bz $div_skip_restore_den ;; We have to restore the denominator [sp+8] movw ax, sp addw ax, #8 NEG_AX div_skip_restore_den: ret END_FUNC ___divsi3 START_FUNC ___modsi3 ;; r8 = 4[sp] % 8[sp] movw de, #0 mov a, [sp+7] mov1 cy, a.7 bc $mod_signed_num mov a, [sp+11] mov1 cy, a.7 bc $mod_signed_den call $!__generic_simod ret mod_signed_num: ;; neg [sp+4] movw ax, sp addw ax, #4 NEG_AX mov d, #1 mov a, [sp+11] mov1 cy, a.7 bnc $mod_unsigned_den mod_signed_den: ;; neg [sp+8] movw ax, sp addw ax, #8 NEG_AX mov e, #1 mod_unsigned_den: call $!__generic_simod mov a, d cmp0 a bz $mod_no_neg movw ax, #r8 NEG_AX ;; We have to restore [sp+4] as well. movw ax, sp addw ax, #4 NEG_AX mod_no_neg: .if 1 mov a, e cmp0 a bz $mod_skip_restore_den movw ax, sp addw ax, #8 NEG_AX mod_skip_restore_den: .endif ret END_FUNC ___modsi3 ;---------------------------------------------------------------------- #else #error "Unknown RL78 hardware multiply/divide support" #endif