72ed112686
* 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
1077 lines
20 KiB
ArmAsm
1077 lines
20 KiB
ArmAsm
/* SImode 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 ___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
|
|
movw numL, #0
|
|
movw numH, #0
|
|
ret
|
|
|
|
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
|