Avoid a software 64-bit divide in pico_printf %ll on RP2350

Printing a long long did value % base and value /= base, which on
RP2350 pulls libgcc's 64-bit divide into every program using printf,
and libgcc runs from RAM (Arm: 880 bytes; RISC-V: separate divide and
modulo, 3.2KB). Divide by the small base in 16-bit pieces with 32-bit
divides instead. RP2040 keeps the plain divide, as pico_divider's
64-bit divide is in flash and often already linked.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
diff --git a/src/rp2_common/pico_printf/printf.c b/src/rp2_common/pico_printf/printf.c
index 2a1022b..6cc00b6 100644
--- a/src/rp2_common/pico_printf/printf.c
+++ b/src/rp2_common/pico_printf/printf.c
@@ -283,6 +283,26 @@
 // internal itoa for 'long long' type
 #if PICO_PRINTF_SUPPORT_LONG_LONG
 
+// RP2040 has a fast/small software implementation for C divide using the hardware divider already
+#if PICO_32BIT && !PICO_RP2040
+#define USE_DIVMOD_U64_BY_SMALL 1
+#endif
+
+#if USE_DIVMOD_U64_BY_SMALL
+// divide by a small base using 32-bit divides, avoiding a software 64-bit divide
+static unsigned int _divmod_u64_by_small(unsigned long long *value, unsigned int base) {
+    uint32_t hi = (uint32_t) (*value >> 32u);
+    uint32_t lo = (uint32_t) *value;
+    uint32_t q_hi = hi / base;
+    uint32_t t = ((hi % base) << 16u) | (lo >> 16u);
+    uint32_t q_mid = t / base;
+    t = ((t % base) << 16u) | (lo & 0xffffu);
+    uint32_t q_lo = t / base;
+    *value = ((unsigned long long) q_hi << 32u) | (q_mid << 16u) | q_lo;
+    return t % base;
+}
+#endif
+
 static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long long value,
                               bool negative, unsigned long long base, unsigned int prec, unsigned int width,
                               unsigned int flags) {
@@ -297,9 +317,13 @@
     // write if precision != 0 and value is != 0
     if (!(flags & FLAGS_PRECISION) || value) {
         do {
+#if !USE_DIVMOD_U64_BY_SMALL
             const char digit = (char) (value % base);
-            buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10);
             value /= base;
+#else
+            const char digit = (char) _divmod_u64_by_small(&value, (unsigned int) base);
+#endif
+            buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10);
         } while (value && (len < PICO_PRINTF_NTOA_BUFFER_SIZE));
     }