gcc/libgcc/config/rl78/divmodhi.S
Nick Clifton 72ed112686 rl78-opts.h (enum rl78_mul_types): Add MUL_G14 and MUL_UNINIT.
* config/rl78/rl78-opts.h (enum rl78_mul_types): Add MUL_G14 and
 	MUL_UNINIT.
 	(enum rl78_cpu_type): New.
 	* config/rl78/rl78-virt.md (attr valloc): Add divhi and divsi.
 	(umulhi3_shift_virt): Remove m constraint from operand 1.
 	(umulqihi3_virt): Likewise.
 	* config/rl78/rl78.c (rl78_option_override): Add code to process
 	-mcpu and -mmul options.
 	(rl78_alloc_physical_registers): Add code to handle divhi and
 	divsi valloc attributes.
 	(set_origin): Likewise.
 	* config/rl78/rl78.h (RL78_MUL_G14): Define.
 	(TARGET_G10, TARGET_G13, TARGET_G14): Define.
 	(TARGET_CPU_CPP_BUILTINS): Define __RL78_MUL_xxx__ and
 	__RL78_Gxx__.
 	(ASM_SPEC): Pass -mcpu on to assembler.
 	* config/rl78/rl78.md (mulqi3): Add a clobber of AX.
 	(mulqi3_rl78): Likewise.
 	(mulhi3_g13): Likewise.
 	(mulhi3): Generate the G13 or G14 versions of the insn directly.
 	(mulsi3): Likewise.
 	(mulhi3_g14): Add clobbers of AX and BC.
 	(mulsi3_g14): Likewise.
 	(mulsi3_g13): Likewise.
 	(udivmodhi4, udivmodhi4_g14, udivmodsi4): New patterns.
 	(udivmodsi4_g14, udivmodsi4_g13): New patterns.
 	* config/rl78/rl78.opt (mmul): Initialise value to
 	RL78_MUL_UNINIT.
 	(mcpu): New option.
 	(m13, m14, mrl78): New option aliases.
 	* config/rl78/t-rl78 (MULTILIB_OPTIONS): Add mg13 and mg14.
 	(MULTILIB_DIRNAMES): Add g13 and g14.
 	* doc/invoke.texi: Document -mcpu and -mmul options.

 	* config/rl78/divmodhi.S: Add G14 and G13 versions of the __divhi3
 	and __modhi3 functions.
	* config/rl78/divmodso.S: Add G14 and G13 versions of the
 	__divsi3, __udivsi3, __modsi3 and __umodsi3 functions.

From-SVN: r222142
2015-04-16 07:57:56 +00:00

693 lines
13 KiB
ArmAsm

/* HImode div/mod functions for the GCC support library for the Renesas RL78 processors.
Copyright (C) 2012-2015 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
<http://www.gnu.org/licenses/>. */
#include "vregs.h"
#if defined __RL78_MUL_G14__
START_FUNC ___divhi3
;; r8 = 4[sp] / 6[sp]
;; Test for a negative denumerator.
movw ax, [sp+6]
mov1 cy, a.7
movw de, ax
bc $__div_neg_den
;; Test for a negative numerator.
movw ax, [sp+4]
mov1 cy, a.7
bc $__div_neg_num
;; Neither are negative - we can use the unsigned divide instruction.
__div_no_convert:
push psw
di
divhu
pop psw
movw r8, ax
ret
__div_neg_den:
;; Negate the denumerator (which is in DE)
clrw ax
subw ax, de
movw de, ax
;; Test for a negative numerator.
movw ax, [sp+4]
mov1 cy, a.7
;; 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 an unsigned division.
movw bc, ax
clrw ax
subw ax, bc
br $__div_no_convert
__div_neg_num:
;; Negate the numerator (which is in AX)
;; We know that the denumerator is positive.
movw bc, ax
clrw ax
subw ax, bc
__div_then_convert:
push psw
di
divhu
pop psw
;; Negate result and transfer into r8
movw bc, ax
clrw ax
subw ax, bc
movw r8, ax
ret
END_FUNC ___divhi3
;----------------------------------------------------------------------
START_FUNC ___modhi3
;; r8 = 4[sp] % 6[sp]
;; Test for a negative denumerator.
movw ax, [sp+6]
mov1 cy, a.7
movw de, ax
bc $__mod_neg_den
;; Test for a negative numerator.
movw ax, [sp+4]
mov1 cy, a.7
bc $__mod_neg_num
;; Neither are negative - we can use the unsigned divide instruction.
__mod_no_convert:
push psw
di
divhu
pop psw
movw ax, de
movw r8, ax
ret
__mod_neg_den:
;; Negate the denumerator (which is in DE)
clrw ax
subw ax, de
movw de, ax
;; Test for a negative numerator.
movw ax, [sp+4]
mov1 cy, a.7
;; 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 an unsigned modulo operation.
movw bc, ax
clrw ax
subw ax, bc
br $__mod_then_convert
__mod_neg_num:
;; Negate the numerator (which is in AX)
;; We know that the denumerator is positive.
movw bc, ax
clrw ax
subw ax, bc
__mod_then_convert:
push psw
di
divhu
pop psw
;; Negate result and transfer into r8
clrw ax
subw ax, de
movw r8, ax
ret
END_FUNC ___modhi3
;----------------------------------------------------------------------
#elif defined __RL78_MUL_G13__
;; The G13 S2 core does not have a 16 bit divide peripheral.
;; So instead we perform a 32-bit divide and twiddle the inputs
;; as necessary.
;; 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 src, dest
movw ax, !\src
movw bc, ax
clrw ax
subw ax, bc
movw \dest, ax
.endm
;----------------------------------------------------------------------
START_FUNC ___divhi3
;; r8 = 4[sp] / 6[sp] (signed division)
mov a, #0xC0 ; Set DIVMODE=1 and MACMODE=1
mov !MDUC, a ; This preps the peripheral for division without interrupt generation
clrw ax ; Clear the top 16-bits of the divisor and dividend
movw MDBH, ax
movw MDAH, ax
;; Load and test for a negative denumerator.
movw ax, [sp+6]
movw MDBL, ax
mov1 cy, a.7
bc $__div_neg_den
;; Load and test for a negative numerator.
movw ax, [sp+4]
mov1 cy, a.7
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
ret
__div_neg_den:
;; Negate the denumerator (which is in MDBL)
_Negate MDBL MDBL
;; Load and test for a negative numerator.
movw ax, [sp+4]
mov1 cy, a.7
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 MDAL
br $!__div_no_convert
__div_neg_num:
;; Negate the numerator (which is in MDAL)
;; We know that the denumerator is positive.
_Negate MDAL MDAL
__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
_Negate MDAL r8
ret
END_FUNC ___divhi3
;----------------------------------------------------------------------
START_FUNC ___modhi3
;; r8 = 4[sp] % 6[sp] (signed modulus)
mov a, #0xC0 ; Set DIVMODE=1 and MACMODE=1
mov !MDUC, a ; This preps the peripheral for division without interrupt generation
clrw ax ; Clear the top 16-bits of the divisor and dividend
movw MDBH, ax
movw MDAH, ax
;; Load and test for a negative denumerator.
movw ax, [sp+6]
movw MDBL, ax
mov1 cy, a.7
bc $__mod_neg_den
;; Load and test for a negative numerator.
movw ax, [sp+4]
mov1 cy, a.7
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
ret
__mod_neg_den:
;; Negate the denumerator (which is in MDBL)
_Negate MDBL MDBL
;; Load and test for a negative numerator.
movw ax, [sp+4]
mov1 cy, a.7
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 MDAL
br $!__mod_then_convert
__mod_neg_num:
;; Negate the numerator (which is in MDAL)
;; We know that the denumerator is positive.
_Negate MDAL MDAL
__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
_Negate MDCL r8
ret
END_FUNC ___modhi3
;----------------------------------------------------------------------
START_FUNC ___udivhi3
;; r8 = 4[sp] / 6[sp] (unsigned division)
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] ; Load the dividend
movw MDBL, ax
clrw ax
movw MDAH, ax
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 remainder
movw r8, ax
ret
END_FUNC ___udivhi3
;----------------------------------------------------------------------
START_FUNC ___umodhi3
;; r8 = 4[sp] % 6[sp] (unsigned modulus)
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] ; Load the dividend
movw MDBL, ax
clrw ax
movw MDAH, ax
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
ret
END_FUNC ___umodhi3
;----------------------------------------------------------------------
#elif defined __RL78_MUL_NONE__
.macro MAKE_GENERIC which,need_result
.if \need_result
quot = r8
num = r10
den = r12
bit = r14
.else
num = r8
quot = r10
den = r12
bit = r14
.endif
quotB0 = quot
quotB1 = quot+1
numB0 = num
numB1 = num+1
denB0 = den
denB1 = den+1
bitB0 = bit
bitB1 = bit+1
#define bit bc
#define bitB0 c
#define bitB1 b
START_FUNC __generic_hidivmod\which
num_lt_den\which:
.if \need_result
movw r8, #0
.else
movw ax, [sp+8]
movw r8, ax
.endif
ret
;; These routines leave DE alone - the signed functions use DE
;; to store sign information that must remain intact
.if \need_result
.global __generic_hidiv
__generic_hidiv:
.else
.global __generic_himod
__generic_himod:
.endif
;; (quot,rem) = 8[sp] /% 10[sp]
movw hl, sp
movw ax, [hl+10] ; denH
cmpw ax, [hl+8] ; numH
bh $num_lt_den\which
;; (quot,rem) = 16[sp] /% 20[sp]
;; copy numerator
movw ax, [hl+8]
movw num, ax
;; copy denomonator
movw ax, [hl+10]
movw den, ax
movw ax, den
cmpw ax, #0
bnz $den_not_zero\which
movw num, #0
ret
den_not_zero\which:
.if \need_result
;; zero out quot
movw quot, #0
.endif
;; initialize bit to 1
movw bit, #1
; while (den < num && !(den & (1L << BITS_MINUS_1)))
shift_den_bit\which:
movw ax, den
mov1 cy,a.7
bc $enter_main_loop\which
cmpw ax, num
bh $enter_main_loop\which
;; den <<= 1
; movw ax, den ; already has it from the cmpw above
shlw ax, 1
movw den, ax
;; bit <<= 1
.if \need_result
#ifdef bit
shlw bit, 1
#else
movw ax, bit
shlw ax, 1
movw bit, ax
#endif
.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
main_loop\which:
;; if (num >= den) (cmp den > num)
movw ax, den
cmpw ax, num
bh $next_loop\which
;; num -= den
movw ax, num
subw ax, den
movw num, 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\which:
;; den >>= 1
movw ax, den
shrw ax, 1
movw den, ax
.if \need_result
;; bit >>= 1
movw ax, bit
shrw ax, 1
movw bit, ax
.else
dec bitB0
.endif
enter_main_loop\which:
.if \need_result
movw ax, bit
cmpw ax, #0
.else
cmp0 bitB0
.endif
bnz $main_loop\which
main_loop_done\which:
ret
END_FUNC __generic_hidivmod\which
.endm
;----------------------------------------------------------------------
MAKE_GENERIC _d 1
MAKE_GENERIC _m 0
;----------------------------------------------------------------------
START_FUNC ___udivhi3
;; r8 = 4[sp] / 6[sp]
call $!__generic_hidiv
ret
END_FUNC ___udivhi3
START_FUNC ___umodhi3
;; r8 = 4[sp] % 6[sp]
call $!__generic_himod
ret
END_FUNC ___umodhi3
;----------------------------------------------------------------------
.macro NEG_AX
movw hl, ax
movw ax, #0
subw ax, [hl]
movw [hl], ax
.endm
;----------------------------------------------------------------------
START_FUNC ___divhi3
;; r8 = 4[sp] / 6[sp]
movw de, #0
mov a, [sp+5]
mov1 cy, a.7
bc $div_signed_num
mov a, [sp+7]
mov1 cy, a.7
bc $div_signed_den
call $!__generic_hidiv
ret
div_signed_num:
;; neg [sp+4]
movw ax, sp
addw ax, #4
NEG_AX
mov d, #1
mov a, [sp+7]
mov1 cy, a.7
bnc $div_unsigned_den
div_signed_den:
;; neg [sp+6]
movw ax, sp
addw ax, #6
NEG_AX
mov e, #1
div_unsigned_den:
call $!__generic_hidiv
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
movw ax, sp
addw ax, #6
NEG_AX
div_skip_restore_den:
ret
END_FUNC ___divhi3
START_FUNC ___modhi3
;; r8 = 4[sp] % 6[sp]
movw de, #0
mov a, [sp+5]
mov1 cy, a.7
bc $mod_signed_num
mov a, [sp+7]
mov1 cy, a.7
bc $mod_signed_den
call $!__generic_himod
ret
mod_signed_num:
;; neg [sp+4]
movw ax, sp
addw ax, #4
NEG_AX
mov d, #1
mov a, [sp+7]
mov1 cy, a.7
bnc $mod_unsigned_den
mod_signed_den:
;; neg [sp+6]
movw ax, sp
addw ax, #6
NEG_AX
mod_unsigned_den:
call $!__generic_himod
mov a, d
cmp0 a
bz $mod_no_neg
movw ax, #r8
NEG_AX
;; Also restore numerator
movw ax, sp
addw ax, #4
NEG_AX
mod_no_neg:
mov a, e
cmp0 a
bz $mod_skip_restore_den
movw ax, sp
addw ax, #6
NEG_AX
mod_skip_restore_den:
ret
END_FUNC ___modhi3
;----------------------------------------------------------------------
#else
#error "Unknown RL78 hardware multiply/divide support"
#endif