| /* SImode div/mod functions for the GCC support library for the Renesas RL78 processors. |
| Copyright (C) 2012-2021 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 |
| .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 |