rl78-opts.h (enum rl78_mul_types): Add MUL_G14 and MUL_UNINIT.
authorNick Clifton <nickc@redhat.com>
Thu, 16 Apr 2015 07:57:56 +0000 (07:57 +0000)
committerNick Clifton <nickc@gcc.gnu.org>
Thu, 16 Apr 2015 07:57:56 +0000 (07:57 +0000)
  * 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

13 files changed:
gcc/ChangeLog
gcc/config/rl78/rl78-opts.h
gcc/config/rl78/rl78-virt.md
gcc/config/rl78/rl78.c
gcc/config/rl78/rl78.h
gcc/config/rl78/rl78.md
gcc/config/rl78/rl78.opt
gcc/config/rl78/t-rl78
gcc/doc/invoke.texi
gcc/testsuite/gcc.c-torture/execute/20101011-1.c
libgcc/ChangeLog
libgcc/config/rl78/divmodhi.S
libgcc/config/rl78/divmodsi.S

index a3a2016c99031f2b4040c79ae29e5976afa8bcd0..5ff0a23a1bbff79756966997db98e40ec3cba809 100644 (file)
@@ -1,3 +1,39 @@
+2015-04-16  Nick Clifton  <nickc@redhat.com>
+
+       * 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.
+
 2015-04-16  Richard Biener  <rguenther@suse.de>
 
        * tree-ssa-ccp.c (likely_value): See if we have operands that
        * config/rx/t-rx (MULTILIB_OPTIONS): Add mno-allow-string-insns.
        (MULTILIB_DIRNAMES): Add no-strings.
        * doc/invoke.texi: Document -mno-allow-string-insns.
+
 2015-04-15  Alan Modra  <amodra@gmail.com>
 
        PR target/65408
index 8d98d8daf54785d1d28aa3bf0d2b04f37c86707b..6fa54cc1143759891dd9ee953b3741d7a68cc39a 100644 (file)
@@ -24,7 +24,17 @@ enum rl78_mul_types
 {
   MUL_NONE,
   MUL_RL78,
-  MUL_G13
+  MUL_G13,
+  MUL_G14,
+  MUL_UNINIT
+};
+
+enum rl78_cpu_types
+{
+  CPU_G10,
+  CPU_G13,
+  CPU_G14,
+  CPU_UNINIT
 };
 
 #endif
index e90e156a8d1f0a80cc1afeeb04b0ca10d88b4584..c70beaec539e8d3a3b825830a5b542f42bcdee89 100644 (file)
@@ -28,7 +28,7 @@
 ;; instruction - op1 is of the form "a = op(b)", op2 is "a = b op c"
 ;; etc.
 
-(define_attr "valloc" "op1,op2,ro1,cmp,umul,macax"
+(define_attr "valloc" "op1,op2,ro1,cmp,umul,macax,divhi,divsi"
   (const_string "op2"))
 
 ;;---------- Moving ------------------------
 )
 
 (define_insn "*umulhi3_shift_virt"
-  [(set (match_operand:HI 0 "register_operand" "=vm")
+  [(set (match_operand:HI          0 "register_operand" "=v")
         (mult:HI (match_operand:HI 1 "rl78_nonfar_operand" "%vim")
                  (match_operand:HI 2 "rl78_24_operand" "Ni")))]
   "rl78_virt_insns_ok () && !TARGET_G10"
 )
 
 (define_insn "*umulqihi3_virt"
-  [(set (match_operand:HI 0 "register_operand" "=vm")
+  [(set (match_operand:HI                          0 "register_operand" "=v")
         (mult:HI (zero_extend:HI (match_operand:QI 1 "rl78_nonfar_operand" "%vim"))
                  (zero_extend:HI (match_operand:QI 2 "general_operand" "vim"))))]
   "rl78_virt_insns_ok () && !TARGET_G10"
index a9d5f5aeaa966feb8c9142921d3c202dd7edb76e..64698045e8af8ecfa96ada20ed79045d52daa1ae 100644 (file)
@@ -377,6 +377,48 @@ rl78_option_override (void)
       && strcmp (lang_hooks.name, "GNU GIMPLE"))
     /* Address spaces are currently only supported by C.  */
     error ("-mes0 can only be used with C");
+
+  switch (rl78_cpu_type)
+    {
+    case CPU_UNINIT:
+      rl78_cpu_type = CPU_G14;
+      if (rl78_mul_type == MUL_UNINIT)
+       rl78_mul_type = MUL_NONE;
+      break;
+
+    case CPU_G10:
+      switch (rl78_mul_type)
+       {
+       case MUL_UNINIT: rl78_mul_type = MUL_NONE; break;
+       case MUL_NONE:   break;
+       case MUL_G13:    error ("-mmul=g13 cannot be used with -mcpu=g10"); break;
+       case MUL_G14:    error ("-mmul=g14 cannot be used with -mcpu=g10"); break;
+       }
+      break;
+
+    case CPU_G13:
+      switch (rl78_mul_type)
+       {
+       case MUL_UNINIT: rl78_mul_type = MUL_G13; break;
+       case MUL_NONE:   break;
+       case MUL_G13:   break;
+         /* The S2 core does not have mul/div instructions.  */
+       case MUL_G14:   error ("-mmul=g14 cannot be used with -mcpu=g13"); break;
+       }
+      break;
+
+    case CPU_G14:
+      switch (rl78_mul_type)
+       {
+       case MUL_UNINIT: rl78_mul_type = MUL_G14; break;
+       case MUL_NONE:   break;
+       case MUL_G14:   break;
+       /* The G14 core does not have the hardware multiply peripheral used by the
+          G13 core, hence you cannot use G13 multipliy routines on G14 hardware.  */
+       case MUL_G13:   error ("-mmul=g13 cannot be used with -mcpu=g14"); break;
+       }
+      break;
+    }
 }
 
 /* Most registers are 8 bits.  Some are 16 bits because, for example,
@@ -3514,6 +3556,18 @@ rl78_alloc_physical_registers (void)
          record_content (BC, NULL_RTX);
          record_content (DE, NULL_RTX);
        }
+      else if (valloc_method == VALLOC_DIVHI)
+       {
+         record_content (AX, NULL_RTX);
+         record_content (BC, NULL_RTX);
+       }
+      else if (valloc_method == VALLOC_DIVSI)
+       {
+         record_content (AX, NULL_RTX);
+         record_content (BC, NULL_RTX);
+         record_content (DE, NULL_RTX);
+         record_content (HL, NULL_RTX);
+       }
 
       if (insn_ok_now (insn))
        continue;
@@ -3541,6 +3595,7 @@ rl78_alloc_physical_registers (void)
          break;
        case VALLOC_UMUL:
          rl78_alloc_physical_registers_umul (insn);
+         record_content (AX, NULL_RTX);
          break;
        case VALLOC_MACAX:
          /* Macro that clobbers AX.  */
@@ -3549,6 +3604,18 @@ rl78_alloc_physical_registers (void)
          record_content (BC, NULL_RTX);
          record_content (DE, NULL_RTX);
          break;
+       case VALLOC_DIVSI:
+         rl78_alloc_address_registers_div (insn);
+         record_content (AX, NULL_RTX);
+         record_content (BC, NULL_RTX);
+         record_content (DE, NULL_RTX);
+         record_content (HL, NULL_RTX);
+         break;
+       case VALLOC_DIVHI:
+         rl78_alloc_address_registers_div (insn);
+         record_content (AX, NULL_RTX);
+         record_content (BC, NULL_RTX);
+         break;
        default:
          gcc_unreachable ();
        }
@@ -3863,6 +3930,37 @@ set_origin (rtx pat, rtx_insn * insn, int * origins, int * age)
            age[i] = 0;
          }
     }
+  else if (get_attr_valloc (insn) == VALLOC_DIVHI)
+    {
+      if (dump_file)
+       fprintf (dump_file, "Resetting origin of AX/DE for DIVHI pattern.\n");
+
+      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+       if (i == A_REG
+           || i == X_REG
+           || i == D_REG
+           || i == E_REG
+           || origins[i] == A_REG
+           || origins[i] == X_REG
+           || origins[i] == D_REG
+           || origins[i] == E_REG)
+         {
+           origins[i] = i;
+           age[i] = 0;
+         }
+    }
+  else if (get_attr_valloc (insn) == VALLOC_DIVSI)
+    {
+      if (dump_file)
+       fprintf (dump_file, "Resetting origin of AX/BC/DE/HL for DIVSI pattern.\n");
+
+      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+       if (i <= 7 || origins[i] <= 7)
+         {
+           origins[i] = i;
+           age[i] = 0;
+         }
+    }
 
   if (GET_CODE (src) == ASHIFT
       || GET_CODE (src) == ASHIFTRT
@@ -4087,7 +4185,7 @@ rl78_rtx_costs (rtx   x,
       switch (code)
        {
        case MULT:
-         if (RL78_MUL_RL78)
+         if (RL78_MUL_G14)
            *total = COSTS_N_INSNS (14);
          else if (RL78_MUL_G13)
            *total = COSTS_N_INSNS (29);
@@ -4407,7 +4505,7 @@ rl78_insert_attributes (tree decl, tree *attributes ATTRIBUTE_UNUSED)
       tree type = TREE_TYPE (decl);
       tree attr = TYPE_ATTRIBUTES (type);
       int q = TYPE_QUALS_NO_ADDR_SPACE (type) | ENCODE_QUAL_ADDR_SPACE (ADDR_SPACE_FAR);
-      
+
       TREE_TYPE (decl) = build_type_attribute_qual_variant (type, attr, q);
     }
 }
@@ -4503,7 +4601,7 @@ rl78_flags_already_set (rtx op, rtx operand)
     {
       if (LABEL_P (insn))
        break;
-      
+
       if (! INSN_P (insn))
        continue;
 
index caa7f7f65413698216516947c1293169944689d2..066b0d1323cfca3af56ec87ef5c3b2b14be3ad1f 100644 (file)
 \f
 
 #define RL78_MUL_NONE  (rl78_mul_type == MUL_NONE)
-#define RL78_MUL_RL78  (rl78_mul_type == MUL_RL78)
 #define RL78_MUL_G13   (rl78_mul_type == MUL_G13)
+#define RL78_MUL_G14   (rl78_mul_type == MUL_G14)
+
+#define TARGET_G10     (rl78_cpu_type == CPU_G10)
+#define TARGET_G13     (rl78_cpu_type == CPU_G13)
+#define TARGET_G14     (rl78_cpu_type == CPU_G14)
 
 #define TARGET_CPU_CPP_BUILTINS()               \
   do                                            \
     {                                           \
       builtin_define ("__RL78__");             \
       builtin_assert ("cpu=RL78");             \
-      if (RL78_MUL_RL78)                       \
-       builtin_define ("__RL78_MUL_RL78__");   \
-      if (RL78_MUL_G13)                                \
+                                               \
+      if (RL78_MUL_NONE)                       \
+       builtin_define ("__RL78_MUL_NONE__");   \
+      else if (RL78_MUL_G13)                   \
        builtin_define ("__RL78_MUL_G13__");    \
+      else if (RL78_MUL_G14)                   \
+       builtin_define ("__RL78_MUL_G14__");    \
+                                               \
       if (TARGET_G10)                          \
        builtin_define ("__RL78_G10__");        \
+      else if (TARGET_G13)                     \
+       builtin_define ("__RL78_G13__");        \
+      else if (TARGET_G14)                     \
+       builtin_define ("__RL78_G14__");        \
     }                                           \
   while (0)
 
 #undef  ASM_SPEC
 #define ASM_SPEC "\
 %{mrelax:-relax} \
-%{mg10} \
+%{mg10:--mg10} \
+%{mg13:--mg13} \
+%{mg14:--mg14} \
+%{mrl78:--mg14} \
+%{mcpu=g10:--mg10} \
+%{mcpu=g13:--mg13} \
+%{mcpu=g14:--mg14} \
+%{mcpu=rl78:--mg14} \
 "
 
 #undef  LINK_SPEC
 */
 #define REGISTER_NAMES                                         \
   {                                                            \
-    "x", "a", "c", "b", "e", "d", "l", "h",                    \
-    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",      \
+    "x",   "a",   "c",   "b",   "e",   "d",   "l",   "h",      \
+    "r8",  "r9",  "r10", "r11", "r12", "r13", "r14", "r15",    \
     "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",    \
     "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",    \
-      "sp", "ap", "psw", "es", "cs"                            \
+    "sp",  "ap",  "psw", "es",  "cs"                           \
   }
 
 #define ADDITIONAL_REGISTER_NAMES      \
index 258c139c86734f65765c1143853fe047f90695ec..2ea788e5817b1c77b4cfa914c670f76e9861a581 100644 (file)
 )
 
 (define_expand "mulqi3"
-  [(set (match_operand:QI          0 "register_operand")
-       (mult:QI  (match_operand:QI 1 "general_operand")
-                 (match_operand:QI 2 "nonmemory_operand")))
-   ]
+  [(parallel
+    [(set (match_operand:QI            0 "register_operand")
+          (mult:QI  (match_operand:QI 1 "general_operand")
+                    (match_operand:QI 2 "nonmemory_operand")))
+      (clobber (reg:HI AX_REG))
+    ])
+  ]
   "" ; mulu supported by all targets
   ""
 )
                 (match_operand:HI 2 "nonmemory_operand")))
    ]
   "! RL78_MUL_NONE"
-  ""
+  {
+    if (RL78_MUL_G14)
+      emit_insn (gen_mulhi3_g14 (operands[0], operands[1], operands[2]));
+    else /* RL78_MUL_G13 */
+      emit_insn (gen_mulhi3_g13 (operands[0], operands[1], operands[2]));
+    DONE;
+  }
 )
 
 (define_expand "mulsi3"
                 (match_operand:SI 2 "nonmemory_operand")))
    ]
   "! RL78_MUL_NONE"
-  ""
+  {
+    if (RL78_MUL_G14)
+      emit_insn (gen_mulsi3_g14 (operands[0], operands[1], operands[2]));
+    else /* RL78_MUL_G13 */
+      emit_insn (gen_mulsi3_g13 (operands[0], operands[1], operands[2]));
+    DONE;
+  }
 )
 
 (define_insn "*mulqi3_rl78"
   [(set (match_operand:QI          0 "register_operand" "=&v")
        (mult:QI (match_operand:QI 1 "general_operand" "viU")
                 (match_operand:QI 2 "general_operand" "vi")))
-   ]
+   (clobber (reg:HI AX_REG))
+  ]
   "" ; mulu supported by all targets
   "; mulqi macro %0 = %1 * %2
        mov    a, %h1
        mov    a, x
        mov    %h0, a
        ; end of mulqi macro"
-;;  [(set_attr "valloc" "macax")]
+  [(set_attr "valloc" "macax")]
 )
 
-(define_insn "*mulhi3_rl78"
+(define_insn "mulhi3_g14"
   [(set (match_operand:HI          0 "register_operand" "=&v")
        (mult:HI (match_operand:HI 1 "general_operand" "viU")
                 (match_operand:HI 2 "general_operand" "vi")))
-   ]
-  "RL78_MUL_RL78"
-  "; mulhi macro %0 = %1 * %2
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI BC_REG))
+  ]
+  "RL78_MUL_G14"
+  "; G14 mulhi macro %0 = %1 * %2
        movw    ax, %h1
        movw    bc, %h2
        mulhu   ; bcax = bc * ax
        movw    %h0, ax
        ; end of mulhi macro"
-;;  [(set_attr "valloc" "macax")]
+  [(set_attr "valloc" "macax")]
 )
 
-(define_insn "*mulhi3_g13"
+(define_insn "mulhi3_g13"
   [(set (match_operand:HI          0 "register_operand" "=&v")
        (mult:HI (match_operand:HI 1 "general_operand" "viU")
                 (match_operand:HI 2 "general_operand" "vi")))
-   ]
+   (clobber (reg:HI AX_REG))
+  ]
   "RL78_MUL_G13"
-  "; mulhi macro %0 = %1 * %2
+  "; G13 mulhi macro %0 = %1 * %2
        mov     a, #0x00
        mov     !0xf00e8, a     ; MDUC
        movw    ax, %h1
        movw    ax, 0xffff6     ; MDBL
        movw    %h0, ax
         ; end of mulhi macro"
-;;  [(set_attr "valloc" "umul")]
+  [(set_attr "valloc" "macax")]
 )
 
 ;; 0xFFFF0 is MACR(L).  0xFFFF2 is MACR(H) but we don't care about it
 ;; because we're only using the lower 16 bits (which is the upper 16
 ;; bits of the result).
-(define_insn "mulsi3_rl78"
+(define_insn "mulsi3_g14"
   [(set (match_operand:SI          0 "register_operand" "=&v")
        (mult:SI (match_operand:SI 1 "general_operand" "viU")
                 (match_operand:SI 2 "general_operand" "vi")))
-   ]
-  "RL78_MUL_RL78"
-  "; mulsi macro %0 = %1 * %2
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI BC_REG))
+  ]
+  "RL78_MUL_G14"
+  "; G14 mulsi macro %0 = %1 * %2
        movw    ax, %h1
        movw    bc, %h2
        MULHU   ; bcax = bc * ax
   [(set (match_operand:SI          0 "register_operand" "=&v")
        (mult:SI (match_operand:SI 1 "general_operand" "viU")
                 (match_operand:SI 2 "general_operand" "viU")))
-   ]
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI BC_REG))
+  ]
   "RL78_MUL_G13"
-  "; mulsi macro %0 = %1 * %2
+  "; G13 mulsi macro %0 = %1 * %2
        mov     a, #0x00
        mov     !0xf00e8, a     ; MDUC
        movw    ax, %h1
        ; end of mulsi macro"
   [(set_attr "valloc" "macax")]
 )
+
+(define_expand "udivmodhi4"
+  [(parallel
+    [(set (match_operand:HI          0 "register_operand")
+          (udiv:HI (match_operand:HI 1 "register_operand")
+                   (match_operand:HI 2 "register_operand")))
+     (set (match_operand:HI          3 "register_operand")
+          (umod:HI (match_dup 1) (match_dup 2)))
+     (clobber (reg:HI AX_REG))
+     (clobber (reg:HI DE_REG))
+    ])
+   ]
+  "RL78_MUL_G14"
+  ""
+)
+
+(define_insn "*udivmodhi4_g14"
+  [(set (match_operand:HI          0 "register_operand" "=v")
+       (udiv:HI (match_operand:HI 1 "register_operand" "v")
+                (match_operand:HI 2 "register_operand" "v")))
+   (set (match_operand:HI          3 "register_operand" "=v")
+       (umod:HI (match_dup 1) (match_dup 2)))
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI DE_REG))
+  ]
+  "RL78_MUL_G14"
+  {
+    if (find_reg_note (insn, REG_UNUSED, operands[3]))
+      return "; G14 udivhi macro %0 = %1 / %2 \n\
+       movw    ax, %h1 \n\
+       movw    de, %h2 \n\
+       push    psw     ; Save the current interrupt status \n\
+       di              ; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E \n\
+       divhu           ; ax = ax / de \n\
+       pop     psw     ; Restore saved interrupt status \n\
+       movw    %h0, ax \n\
+       ; end of udivhi macro";
+    else if (find_reg_note (insn, REG_UNUSED, operands[0]))
+      return "; G14 umodhi macro %3 = %1 %% %2 \n\
+       movw    ax, %h1 \n\
+       movw    de, %h2 \n\
+       push    psw     ; Save the current interrupt status \n\
+       di              ; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E \n\
+       divhu           ; de = ax %% de \n\
+       pop     psw     ; Restore saved interrupt status \n\
+       movw    ax, de \n\
+       movw    %h3, ax \n\
+       ; end of umodhi macro";
+    else
+      return "; G14 udivmodhi macro %0 = %1 / %2 and %3 = %1 %% %2 \n\
+       movw    ax, %h1 \n\
+       movw    de, %h2 \n\
+       push    psw     ; Save the current interrupt status \n\
+       di              ; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E \n\
+       divhu           ; ax = ax / de, de = ax %% de \n\
+       pop     psw     ; Restore saved interrupt status \n\
+       movw    %h0, ax \n\
+       movw    ax, de \n\
+       movw    %h3, ax \n\
+       ; end of udivmodhi macro";
+  }
+  [(set_attr "valloc" "divhi")]
+)
+
+(define_expand "udivmodsi4"
+  [(parallel
+    [(set (match_operand:SI          0 "register_operand")
+          (udiv:SI (match_operand:SI 1 "register_operand")
+                   (match_operand:SI 2 "register_operand")))
+     (set (match_operand:SI          3 "register_operand")
+          (umod:SI (match_dup 1) (match_dup 2)))
+    ])
+   ]
+  "! RL78_MUL_NONE && ! optimize_size"
+  {
+    if (RL78_MUL_G14)
+      emit_insn (gen_udivmodsi4_g14 (operands[0], operands[1], operands[2], operands[3]));
+    else /* RL78_MUL_G13 */
+      emit_insn (gen_udivmodsi4_g13 (operands[0], operands[1], operands[2], operands[3]));
+    DONE;
+  }
+)
+
+(define_insn "udivmodsi4_g14"
+  [(set (match_operand:SI          0 "register_operand" "=v")
+       (udiv:SI (match_operand:SI 1 "register_operand" "v")
+                (match_operand:SI 2 "register_operand" "v")))
+   (set (match_operand:SI          3 "register_operand" "=v")
+       (umod:SI (match_dup 1) (match_dup 2)))
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI BC_REG))
+   (clobber (reg:HI DE_REG))
+   (clobber (reg:HI HL_REG))
+  ]
+  "RL78_MUL_G14"
+  {
+    if (find_reg_note (insn, REG_UNUSED, operands[3]))
+      return "; G14 udivsi macro %0 = %1 / %2 \n\
+       movw    ax, %h1 \n\
+       movw    bc, %H1 \n\
+       movw    de, %h2 \n\
+       movw    hl, %H2 \n\
+       push    psw     ; Save the current interrupt status \n\
+       di              ; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E \n\
+       divwu           ; bcax = bcax / hlde \n\
+       pop     psw     ; Restore saved interrupt status \n\
+       movw    %h0, ax \n\
+       movw    ax, bc \n\
+       movw    %H0, ax \n\
+       ; end of udivsi macro";
+    else if (find_reg_note (insn, REG_UNUSED, operands[0]))
+      return "; G14 umodsi macro %3 = %1 %% %2 \n\
+       movw    ax, %h1 \n\
+       movw    bc, %H1 \n\
+       movw    de, %h2 \n\
+       movw    hl, %H2 \n\
+       push    psw     ; Save the current interrupt status \n\
+       di              ; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E \n\
+       divwu           ; hlde = bcax %% hlde \n\
+       pop     psw     ; Restore saved interrupt status \n\
+       movw    ax, de \n\
+       movw    %h3, ax \n\
+       movw    ax, hl \n\
+       movw    %H3, ax \n\
+       ; end of umodsi macro";
+    else
+      return "; G14 udivmodsi macro %0 = %1 / %2 and %3 = %1 %% %2 \n\
+       movw    ax, %h1 \n\
+       movw    bc, %H1 \n\
+       movw    de, %h2 \n\
+       movw    hl, %H2 \n\
+       push    psw     ; Save the current interrupt status \n\
+       di              ; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E \n\
+       divwu           ; bcax = bcax / hlde, hlde = bcax %% hlde \n\
+       pop     psw     ; Restore saved interrupt status \n\
+       movw    %h0, ax \n\
+       movw    ax, bc \n\
+       movw    %H0, ax \n\
+       movw    ax, de \n\
+       movw    %h3, ax \n\
+       movw    ax, hl \n\
+       movw    %H3, ax \n\
+       ; end of udivmodsi macro";
+  }
+  [(set_attr "valloc" "divsi")]
+)
+
+;; Warning: these values match the silicon not the documentation.
+;; 0xFFFF0 is MDAL.  0xFFFF2 is MDAH.
+;; 0xFFFF6 is MDBL.  0xFFFF4 is MDBH.
+;; 0xF00E0 is MDCL.  0xF00E2 is MDCH.
+;; 0xF00E8 is MDUC.
+
+(define_insn "udivmodsi4_g13"
+  [(set (match_operand:SI          0 "register_operand" "=v")
+       (udiv:SI (match_operand:SI 1 "register_operand" "v")
+                (match_operand:SI 2 "register_operand" "v")))
+   (set (match_operand:SI          3 "register_operand" "=v")
+       (umod:SI (match_dup 1) (match_dup 2)))
+   (clobber (reg:HI AX_REG))
+  ]
+  "RL78_MUL_G13"
+  {
+    if (find_reg_note (insn, REG_UNUSED, operands[3]))
+      return "; G13 udivsi macro %0 = %1 / %2 \n\
+       mov     a, #0xC0        ; Set DIVMODE=1 and MACMODE=1 \n\
+       mov     !0xf00e8, a     ; This preps the peripheral for division without interrupt generation \n\
+       movw    ax, %H1         \n\
+       movw    0xffff2, ax     ; MDAH \n\
+       movw    ax, %h1         \n\
+       movw    0xffff0, ax     ; MDAL \n\
+       movw    ax, %H2         \n\
+       movw    0xffff4, ax     ; MDBH \n\
+       movw    ax, %h2         \n\
+       movw    0xffff6, ax     ; MDBL \n\
+       mov     a, #0xC1        ; Set the DIVST bit in MDUC \n\
+       mov     !0xf00e8, a     ; This starts the division op \n\
+1:     mov     a, !0xf00e8     ; Wait 16 clocks or until DIVST is clear \n\
+       bt      a.0, $1b        \n\
+       movw    ax, 0xffff0     ; Read the quotient \n\
+       movw    %h0, ax         \n\
+       movw    ax, 0xffff2     \n\
+       movw    %H0, ax         \n\
+       ; end of udivsi macro";
+    else if (find_reg_note (insn, REG_UNUSED, operands[0]))
+      return "; G13 umodsi macro %3 = %1 %% %2 \n\
+       mov     a, #0xC0        ; Set DIVMODE=1 and MACMODE=1 \n\
+       mov     !0xf00e8, a     ; This preps the peripheral for division without interrupt generation \n\
+       movw    ax, %H1         \n\
+       movw    0xffff2, ax     ; MDAH \n\
+       movw    ax, %h1         \n\
+       movw    0xffff0, ax     ; MDAL \n\
+       movw    ax, %H2         \n\
+       movw    0xffff4, ax     ; MDBH \n\
+       movw    ax, %h2         \n\
+       movw    0xffff6, ax     ; MDBL \n\
+       mov     a, #0xC1        ; Set the DIVST bit in MDUC \n\
+       mov     !0xf00e8, a     ; This starts the division op \n\
+1:     mov     a, !0xf00e8     ; Wait 16 clocks or until DIVST is clear \n\
+       bt      a.0, $1b        \n\
+       movw    ax, !0xf00e0    ; Read the remainder \n\
+       movw    %h3, ax         \n\
+       movw    ax, !0xf00e2    \n\
+       movw    %H3, ax         \n\
+       ; end of umodsi macro";
+    else
+      return "; G13 udivmodsi macro %0 = %1 / %2 and %3 = %1 %% %2 \n\
+       mov     a, #0xC0        ; Set DIVMODE=1 and MACMODE=1 \n\
+       mov     !0xf00e8, a     ; This preps the peripheral for division without interrupt generation \n\
+       movw    ax, %H1         \n\
+       movw    0xffff2, ax     ; MDAH \n\
+       movw    ax, %h1         \n\
+       movw    0xffff0, ax     ; MDAL \n\
+       movw    ax, %H2         \n\
+       movw    0xffff4, ax     ; MDBH \n\
+       movw    ax, %h2         \n\
+       movw    0xffff6, ax     ; MDBL \n\
+       mov     a, #0xC1        ; Set the DIVST bit in MDUC \n\
+       mov     !0xf00e8, a     ; This starts the division op \n\
+1:     mov     a, !0xf00e8     ; Wait 16 clocks or until DIVST is clear \n\
+       bt      a.0, $1b        \n\
+       movw    ax, 0xffff0     ; Read the quotient \n\
+       movw    %h0, ax         \n\
+       movw    ax, 0xffff2     \n\
+       movw    %H0, ax         \n\
+       movw    ax, !0xf00e0    ; Read the remainder \n\
+       movw    %h3, ax         \n\
+       movw    ax, !0xf00e2    \n\
+       movw    %H3, ax         \n\
+       ; end of udivmodsi macro";
+      }
+  [(set_attr "valloc" "macax")]
+)
index 1e4c14c706e0774619d9eb9dfb1c113dc05ec78f..80b894a6e674598aa111d3189bd0e57577f1ea4a 100644 (file)
@@ -27,20 +27,23 @@ Target Report
 Use the simulator runtime.
 
 mmul=
-Target RejectNegative Joined Var(rl78_mul_type) Report Tolower Enum(rl78_mul_types) Init(MUL_NONE)
-Select hardware or software multiplication support.
+Target RejectNegative Joined Var(rl78_mul_type) Report Tolower Enum(rl78_mul_types) Init(MUL_UNINIT)
+Selects the type of hardware multiplication and division to use (none/g13/g14).
 
 Enum
 Name(rl78_mul_types) Type(enum rl78_mul_types)
 
 EnumValue
-Enum(rl78_mul_types) String(none) Value(MUL_NONE)
+Enum(rl78_mul_types) String(g10) Value(MUL_NONE)
 
 EnumValue
-Enum(rl78_mul_types) String(rl78) Value(MUL_RL78)
+Enum(rl78_mul_types) String(g13) Value(MUL_G13)
 
 EnumValue
-Enum(rl78_mul_types) String(g13) Value(MUL_G13)
+Enum(rl78_mul_types) String(g14) Value(MUL_G14)
+
+EnumValue
+Enum(rl78_mul_types) String(rl78) Value(MUL_G14)
 
 mallregs
 Target Mask(ALLREGS) Report Optimization
@@ -50,9 +53,40 @@ mrelax
 Target Report Optimization
 Enable assembler and linker relaxation.  Enabled by default at -Os.
 
+mcpu=
+Target RejectNegative Joined Var(rl78_cpu_type) Report ToLower Enum(rl78_cpu_types) Init(CPU_UNINIT)
+Selects the type of RL78 core being targeted (g10/g13/g14).  The default is the G14.  If set, also selects the hardware multiply support to be used.
+
+Enum
+Name(rl78_cpu_types) Type(enum rl78_cpu_types)
+
+EnumValue
+Enum(rl78_cpu_types) String(g10) Value(CPU_G10)
+
+EnumValue
+Enum(rl78_cpu_types) String(g13) Value(CPU_G13)
+
+EnumValue
+Enum(rl78_cpu_types) String(g14) Value(CPU_G14)
+
+EnumValue
+Enum(rl78_cpu_types) String(rl78) Value(CPU_G14)
+
 mg10
-Target Mask(G10) Report
-Target the RL78/G10 series
+Target RejectNegative Report Alias(mcpu=, g10)
+Alias for -mcpu=g10
+
+mg13
+Target RejectNegative Report Alias(mcpu=, g13)
+Alias for -mcpu=g13
+
+mg14
+Target RejectNegative Report Alias(mcpu=, g14)
+Alias for -mcpu=g14
+
+mrl78
+Target RejectNegative Report Alias(mcpu=, g14)
+Alias for -mcpu=g14
 
 mes0
 Target Mask(ES0)
index 262885564d1e9ad145009e270ff6c59346c321bc..bd8fab430e0f4c227e2e8032251d1283e730878d 100644 (file)
@@ -23,5 +23,7 @@ rl78-c.o: $(srcdir)/config/rl78/rl78-c.c $(RTL_H) $(TREE_H) $(CONFIG_H) $(TM_H)
 
 # Enable multilibs:
 
-MULTILIB_OPTIONS    = mg10
-MULTILIB_DIRNAMES   = g10
+MULTILIB_OPTIONS    = mg10/mg13/mg14
+MULTILIB_DIRNAMES   = g10 g13 g14
+
+MULTILIB_MATCHES    = mg10=mcpu?g10 mg13=mcpu?g13 mg14=mcpu?g14 mg14=mcpu?rl78
index 18f6990624865f1e221b9cfae4a5b2a9bc555df1..bb173854663c3c8196b48879d64bcc1beedae910 100644 (file)
@@ -265,7 +265,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wmain -Wmaybe-uninitialized -Wmemset-transposed-args -Wmissing-braces @gol
 -Wmissing-field-initializers -Wmissing-include-dirs @gol
 -Wno-multichar  -Wnonnull  -Wnormalized=@r{[}none@r{|}id@r{|}nfc@r{|}nfkc@r{]} @gol
- -Wodr  -Wno-overflow  -Wopenmp-simd @gol
+-Wodr  -Wno-overflow  -Wopenmp-simd @gol
 -Woverlength-strings  -Wpacked  -Wpacked-bitfield-compat  -Wpadded @gol
 -Wparentheses  -Wpedantic-ms-format -Wno-pedantic-ms-format @gol
 -Wpointer-arith  -Wno-pointer-to-int-cast @gol
@@ -277,7 +277,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wstack-protector -Wstack-usage=@var{len} -Wstrict-aliasing @gol
 -Wstrict-aliasing=n @gol -Wstrict-overflow -Wstrict-overflow=@var{n} @gol
 -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]} @gol
--Wsuggest-final-types @gol -Wsuggest-final-methods @gol -Wsuggest-override @gol
+-Wsuggest-final-types @gol -Wsuggest-final-methods -Wsuggest-override @gol
 -Wmissing-format-attribute @gol
 -Wswitch  -Wswitch-default  -Wswitch-enum -Wswitch-bool -Wsync-nand @gol
 -Wsystem-headers  -Wtrampolines  -Wtrigraphs  -Wtype-limits  -Wundef @gol
@@ -405,7 +405,7 @@ Objective-C and Objective-C++ Dialects}.
 -fisolate-erroneous-paths-dereference -fisolate-erroneous-paths-attribute @gol
 -fivopts -fkeep-inline-functions -fkeep-static-consts @gol
 -flive-range-shrinkage @gol
--floop-block -floop-interchange -floop-strip-mine @gol  
+-floop-block -floop-interchange -floop-strip-mine @gol
 -floop-unroll-and-jam -floop-nest-optimize @gol
 -floop-parallelize-all -flra-remat -flto -flto-compression-level @gol
 -flto-partition=@var{alg} -flto-report -flto-report-wpa -fmerge-all-constants @gol
@@ -870,7 +870,8 @@ Objective-C and Objective-C++ Dialects}.
 See RS/6000 and PowerPC Options.
 
 @emph{RL78 Options}
-@gccoptlist{-msim -mmul=none -mmul=g13 -mmul=rl78 @gol
+@gccoptlist{-msim -mmul=none -mmul=g13 -mmul=g14 -mallregs @gol
+-mcpu=g10 -mcpu=g13 -mcpu=g14 -mg10 -mg13 -mg14 @gol
 -m64bit-doubles -m32bit-doubles}
 
 @emph{RS/6000 and PowerPC Options}
@@ -1786,7 +1787,7 @@ Using this option is roughly equivalent to adding the
 
 The option @option{-fno-gnu89-inline} explicitly tells GCC to use the
 C99 semantics for @code{inline} when in C99 or gnu99 mode (i.e., it
-specifies the default behavior).  
+specifies the default behavior).
 This option is not supported in @option{-std=c90} or
 @option{-std=gnu90} mode.
 
@@ -2090,7 +2091,7 @@ Version 0 refers to the version conforming most closely to
 the C++ ABI specification.  Therefore, the ABI obtained using version 0
 will change in different versions of G++ as ABI bugs are fixed.
 
-Version 1 is the version of the C++ ABI that first appeared in G++ 3.2.  
+Version 1 is the version of the C++ ABI that first appeared in G++ 3.2.
 
 Version 2 is the version of the C++ ABI that first appeared in G++
 3.4, and was the default through G++ 4.9.
@@ -4354,7 +4355,6 @@ pointers. This warning level may give a larger number of
 false positives and is deactivated by default.
 @end table
 
-
 @item -Wbool-compare
 @opindex Wno-bool-compare
 @opindex Wbool-compare
@@ -5890,7 +5890,7 @@ Bounds Checker builtins}, for more information.
 @opindex fchkp-check-incomplete-type
 @opindex fno-chkp-check-incomplete-type
 Generate pointer bounds checks for variables with incomplete type.
-Enabled by default.  
+Enabled by default.
 
 @item -fchkp-narrow-bounds
 @opindex fchkp-narrow-bounds
@@ -18730,14 +18730,72 @@ Links in additional target libraries to support operation within a
 simulator.
 
 @item -mmul=none
+@itemx -mmul=g10
 @itemx -mmul=g13
+@itemx -mmul=g14
 @itemx -mmul=rl78
 @opindex mmul
-Specifies the type of hardware multiplication support to be used.  The
-default is @samp{none}, which uses software multiplication functions.
-The @samp{g13} option is for the hardware multiply/divide peripheral
-only on the RL78/G13 targets.  The @samp{rl78} option is for the
-standard hardware multiplication defined in the RL78 software manual.
+Specifies the type of hardware multiplication and division support to
+be used.  The simplest is @code{none}, which uses software for both
+multiplication and division.  This is the default.  The @code{g13}
+value is for the hardware multiply/divide peripheral found on the
+RL78/G13 (S2 core) targets.  The @code{g14} value selects the use of
+the multiplication and division instructions supported by the RL78/G14
+(S3 core) parts.  The value @code{rl78} is an alias for @code{g14} and
+the value @code{mg10} is an alias for @code{none}.
+
+In addition a C preprocessor macro is defined, based upon the setting
+of this option.  Possible values are: @code{__RL78_MUL_NONE__},
+@code{__RL78_MUL_G13__} or @code{__RL78_MUL_G14__}.
+
+@item -mcpu=g10
+@itemx -mcpu=g13
+@itemx -mcpu=g14
+@itemx -mcpu=rl78
+@opindex mcpu
+Specifies the RL78 core to target.  The default is the G14 core, also
+known as an S3 core or just RL78.  The G13 or S2 core does not have
+multiply or divide instructions, instead it uses a hardware peripheral
+for these operations.  The G10 or S1 core does not have register
+banks, so it uses a different calling convention.
+
+If this option is set it also selects the type of hardware multiply
+support to use, unless this is overridden by an explicit
+@option{-mmul=none} option on the command line.  Thus specifying
+@option{-mcpu=g13} enables the use of the G13 hardware multiply
+peripheral and specifying @option{-mcpu=g10} disables the use of
+hardware multipications altogether.
+
+Note, although the RL78/G14 core is the default target, specifying
+@option{-mcpu=g14} or @option{-mcpu=rl78} on the command line does
+change the behaviour of the toolchain since it also enables G14
+hardware multiply support.  If these options are not specified on the
+command line then software multiplication routines will be used even
+though the code targets the RL78 core.  This is for backwards
+compatibility with older toolchains which did not have hardware
+multiply and divide support.
+
+In addition a C preprocessor macro is defined, based upon the setting
+of this option.  Possible values are: @code{__RL78_G10__},
+@code{__RL78_G13__} or @code{__RL78_G14__}.
+
+@item -mg10
+@itemx -mg13
+@itemx -mg14
+@itemx -mrl78
+@opindex mg10
+@opindex mg13
+@opindex mg14
+@opindex mrl78
+These are aliases for the corresponding @option{-mcpu=} option.  They
+are provided for backwards compatibility.
+
+@item -mallregs
+@opindex mallregs
+Allow the compiler to use all of the available registers.  By default
+registers @code{r24..r31} are reserved for use in interrupt handlers.
+With this option enabled these registers can be used in ordinary
+functions as well.
 
 @item -m64bit-doubles
 @itemx -m32bit-doubles
index 34d03137e26e62b62810978adb5416cc7b454f4a..639d12de6832d49e35b199d06e9ebf3e5cf909a1 100644 (file)
 #elif defined (__sh__)
   /* On SH division by zero does not trap.  */
 # define DO_TEST 0
+#elif defined (__v850__)
+  /* On V850 division by zero does not trap.  */
+# define DO_TEST 0
+#elif defined (__MSP430__)
+  /* On MSP430 division by zero does not trap.  */
+# define DO_TEST 0
+#elif defined (__RL78__)
+  /* On RL78 division by zero does not trap.  */
+# define DO_TEST 0
+#elif defined (__RX__)
+  /* On RX division by zero does not trap.  */
+# define DO_TEST 0
 #elif defined (__aarch64__)
   /* On AArch64 integer division by zero does not trap.  */
 # define DO_TEST 0
index 611223ab645ea201f615493a6a49c74f838cb9d4..a21afe86fc51c118c22779f8cb930ca4081bb366 100644 (file)
@@ -1,3 +1,10 @@
+2015-04-16  Nick Clifton  <nickc@redhat.com>
+
+       * 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.
+
 2015-04-15  Chen Gang  <gang.chen.5i5j@gmail.com>
 
        * gthr-single.h (__GTHREAD_MUTEX_INIT_FUNCTION): Use empty
index 4e5f3a2f1e63359fbb13bbe14e3eb0ad54455173..adf91e2c9e624ea820a44a226dc54a3045c7ce60 100644 (file)
 
 #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
@@ -328,3 +682,11 @@ mod_no_neg:
 mod_skip_restore_den:  
        ret
 END_FUNC ___modhi3
+
+;----------------------------------------------------------------------
+
+#else
+
+#error "Unknown RL78 hardware multiply/divide support"
+
+#endif
index 7acaa86340b670f16cda1c03ca5288df7d50da69..987a9e31126a79729aefdefb584e95b8e29fb2f8 100644 (file)
 
 #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
        bitB2 = bit+2
        bitB3 = bit+3
 
+;----------------------------------------------------------------------
+
 START_FUNC __generic_sidivmod\which
 
 num_lt_den\which:
@@ -533,3 +1066,11 @@ mod_skip_restore_den:
  .endif        
        ret
 END_FUNC ___modsi3
+
+;----------------------------------------------------------------------
+
+#else
+
+#error "Unknown RL78 hardware multiply/divide support"
+
+#endif