1/*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#if PICO_RP2040
8#include "pico/asm_helper.S"
9#include "pico/runtime_init.h"
10#include "pico/bootrom/sf_table.h"
11#include "hardware/divider_helper.S"
12
13pico_default_asm_setup
14
15PICO_RUNTIME_INIT_FUNC_RUNTIME(__aeabi_double_init, PICO_RUNTIME_INIT_AEABI_DOUBLE)
16
17.macro double_section name
18#if PICO_DOUBLE_IN_RAM
19.section RAM_SECTION_NAME(\name), "ax"
20#else
21.section SECTION_NAME(\name), "ax"
22#endif
23.endm
24
25.macro _double_wrapper_func x
26    wrapper_func \x
27.endm
28
29.macro wrapper_func_d1 x
30   _double_wrapper_func \x
31#if PICO_DOUBLE_PROPAGATE_NANS
32    mov ip, lr
33    bl __check_nan_d1
34    mov lr, ip
35#endif
36.endm
37
38.macro wrapper_func_d2 x
39    _double_wrapper_func \x
40#if PICO_DOUBLE_PROPAGATE_NANS
41    mov ip, lr
42    bl __check_nan_d2
43    mov lr, ip
44#endif
45.endm
46
47.section .text
48
49#if PICO_DOUBLE_PROPAGATE_NANS
50.thumb_func
51__check_nan_d1:
52   movs r3, #1
53   lsls r3, #21
54   lsls r2, r1, #1
55   adds r2, r3
56   bhi 1f
57   bx lr
581:
59   bx ip
60
61.thumb_func
62__check_nan_d2:
63   push {r0, r2}
64   movs r2, #1
65   lsls r2, #21
66   lsls r0, r1, #1
67   adds r0, r2
68   bhi 1f
69   lsls r0, r3, #1
70   adds r0, r2
71   bhi 2f
72   pop {r0, r2}
73   bx lr
742:
75   pop {r0, r2}
76   mov r0, r2
77   mov r1, r3
78   bx ip
791:
80   pop {r0, r2}
81   bx ip
82#endif
83
84.macro table_tail_call SF_TABLE_OFFSET
85    push {r3, r4}
86#if PICO_DOUBLE_SUPPORT_ROM_V1 && PICO_RP2040_B0_SUPPORTED
87#ifndef NDEBUG
88    movs r3, #0
89    mov ip, r3
90#endif
91#endif
92    ldr r3, =sd_table
93    ldr r3, [r3, #\SF_TABLE_OFFSET]
94    str r3, [sp, #4]
95    pop {r3, pc}
96.endm
97
98.macro shimmable_table_tail_call SF_TABLE_OFFSET shim
99    push {r3, r4}
100    ldr r3, =sd_table
101    ldr r3, [r3, #\SF_TABLE_OFFSET]
102#if PICO_DOUBLE_SUPPORT_ROM_V1 && PICO_RP2040_B0_SUPPORTED
103    mov ip, pc
104#endif
105    str r3, [sp, #4]
106    pop {r3, pc}
107#if PICO_DOUBLE_SUPPORT_ROM_V1 && PICO_RP2040_B0_SUPPORTED
108.byte \SF_TABLE_OFFSET, 0xdf
109.word \shim
110#endif
111.endm
112
113.macro double_wrapper_section func
114double_section WRAPPER_FUNC_NAME(\func)
115.endm
116
117double_section push_r8_r11
118regular_func push_r8_r11
119 mov r4,r8
120 mov r5,r9
121 mov r6,r10
122 mov r7,r11
123 push {r4-r7}
124 bx r14
125
126double_section pop_r8_r11
127regular_func pop_r8_r11
128 pop {r4-r7}
129 mov r8,r4
130 mov r9,r5
131 mov r10,r6
132 mov r11,r7
133 bx r14
134
135// note generally each function is in a separate section unless there is fall thru or branching between them
136// note fadd, fsub, fmul, fdiv are so tiny and just defer to rom so are lumped together so they can share constant pool
137
138// note functions are word aligned except where they are an odd number of linear instructions
139
140// double FUNC_NAME(__aeabi_dadd)(double, double)         double-precision addition
141double_wrapper_section __aeabi_darithmetic
142// double FUNC_NAME(__aeabi_drsub)(double x, double y)    double-precision reverse subtraction, y - x
143
144// frsub first because it is the only one that needs alignment
145.align 2
146wrapper_func __aeabi_drsub
147    eors r0, r1
148    eors r1, r0
149    eors r0, r1
150    // fall thru
151
152// double FUNC_NAME(__aeabi_dsub)(double x, double y)     double-precision subtraction, x - y
153wrapper_func_d2 __aeabi_dsub
154#if PICO_DOUBLE_PROPAGATE_NANS
155    // we want to return nan for inf-inf or -inf - -inf, but without too much upfront cost
156    mov ip, r0
157    mov r0, r1
158    eors r0, r3
159    bmi 1f // different signs
160    mov r0, ip
161    push {r0-r3, lr}
162    bl 2f
163    b ddiv_dsub_nan_helper
1641:
165    mov r0, ip
1662:
167#endif
168   shimmable_table_tail_call SF_TABLE_FSUB dsub_shim
169
170wrapper_func_d2 __aeabi_dadd
171   shimmable_table_tail_call SF_TABLE_FADD dadd_shim
172
173// double FUNC_NAME(__aeabi_ddiv)(double n, double d)     double-precision division, n / d
174wrapper_func_d2 __aeabi_ddiv
175#if PICO_DOUBLE_PROPAGATE_NANS
176    push {r0-r3, lr}
177    bl 1f
178    b ddiv_dsub_nan_helper
1791:
180#endif
181#if !PICO_DIVIDER_DISABLE_INTERRUPTS
182    // to support IRQ usage (or context switch) we must save/restore divider state around call if state is dirty
183    mov ip, r2
184    ldr r2, =(SIO_BASE)
185    ldr r2, [r2, #SIO_DIV_CSR_OFFSET]
186    lsrs r2, #SIO_DIV_CSR_DIRTY_SHIFT_FOR_CARRY
187    bcs ddiv_save_state
188    mov r2, ip
189#else
190    // to avoid worrying about IRQs (or context switches), simply disable interrupts around call
191    push {r4, lr}
192    mrs r4, PRIMASK
193    cpsid i
194    bl ddiv_shim_call
195    msr PRIMASK, r4
196    pop {r4, pc}
197#endif
198ddiv_shim_call:
199    shimmable_table_tail_call SF_TABLE_FDIV ddiv_shim
200
201#if !PICO_DIVIDER_DISABLE_INTERRUPTS
202ddiv_save_state:
203    ldr r2, =(SIO_BASE)
204    save_div_state_and_lr
205    mov r2, ip
206    bl ddiv_shim_call
207    ldr r2, =(SIO_BASE)
208    restore_div_state_and_return
209#endif
210
211ddiv_dsub_nan_helper:
212#if PICO_DOUBLE_PROPAGATE_NANS
213    // check for infinite op infinite (or rather check for infinite result with both
214    // operands being infinite)
215    lsls r2, r1, #1
216    asrs r2, r2, #21
217    adds r2, #1
218    beq 2f
219    add sp, #16
220    pop {pc}
2212:
222    ldr r2, [sp, #4]
223    ldr r3, [sp, #12]
224    lsls r2, #1
225    asrs r2, r2, #21
226    lsls r3, #1
227    asrs r3, r3, #24
228    ands r2, r3
229    adds r2, #1
230    bne 3f
231    // infinite to nan
232    movs r2, #1
233    lsls r2, #19
234    orrs r1, r2
2353:
236    add sp, #16
237    pop {pc}
238#endif
239
240// double FUNC_NAME(__aeabi_dmul)(double, double)         double-precision multiplication
241wrapper_func_d2 __aeabi_dmul
242#if PICO_DOUBLE_PROPAGATE_NANS
243    push {r0-r3, lr}
244    bl 1f
245
246    // check for multiplication of infinite by zero (or rather check for infinite result with either
247    // operand 0)
248    lsls r3, r1, #1
249    asrs r3, r3, #21
250    adds r3, #1
251    beq 2f
252    add sp, #16
253    pop {pc}
2542:
255    ldr r2, [sp, #4]
256    ldr r3, [sp, #12]
257    ands r2, r3
258    bne 3f
259    // infinite to nan
260    movs r2, #1
261    lsls r2, #19
262    orrs r1, r2
2633:
264    add sp, #16
265    pop {pc}
2661:
267#endif
268   shimmable_table_tail_call SF_TABLE_FMUL dmul_shim
269
270// void FUNC_NAME(__aeabi_cdrcmple)(double, double)         reversed 3-way (<, =, ?>) compare [1], result in PSR ZC flags
271double_wrapper_section __aeabi_cdcmple
272
273wrapper_func __aeabi_cdrcmple
274 push {r0-r7,r14}
275    eors r0, r2
276    eors r2, r0
277    eors r0, r2
278    eors r1, r3
279    eors r3, r1
280    eors r1, r3
281    b __aeabi_dfcmple_guts
282
283// NOTE these share an implementation as we have no excepting NaNs.
284// void FUNC_NAME(__aeabi_cdcmple)(double, double)         3-way (<, =, ?>) compare [1], result in PSR ZC flags
285// void FUNC_NAME(__aeabi_cdcmpeq)(double, double)         non-excepting equality comparison [1], result in PSR ZC flags
286@ compare r0:r1 against r2:r3, returning -1/0/1 for <, =, >
287@ also set flags accordingly
288.align 2
289wrapper_func __aeabi_cdcmple
290wrapper_func __aeabi_cdcmpeq
291 push {r0-r7,r14}
292__aeabi_dfcmple_guts:
293 ldr r7,=0x7ff                @ flush NaNs and denormals
294 lsls r4,r1,#1
295 lsrs r4,#21
296 beq 1f
297 cmp r4,r7
298 bne 2f
299 lsls r4, r1, #12
300 bhi 7f
3011:
302 movs r0,#0
303 lsrs r1,#20
304 lsls r1,#20
3052:
306 lsls r4,r3,#1
307 lsrs r4,#21
308 beq 1f
309 cmp r4,r7
310 bne 2f
311 lsls r4, r3, #12
312 bhi 7f
3131:
314 movs r2,#0
315 lsrs r3,#20
316 lsls r3,#20
3172:
318 movs r6,#1
319 eors r3,r1
320 bmi 4f                        @ opposite signs? then can proceed on basis of sign of x
321 eors r3,r1                    @ restore r3
322 bpl 2f
323 cmp r3,r1
324 bne 7f
3251:
326 cmp r2,r0
3277:
328 pop {r0-r7,r15}
3292:
330 cmp r1,r3
331 bne 7b
3321:
333 cmp r0,r2
334 pop {r0-r7,r15}
3354:
336 orrs r3,r1                    @ make -0==+0
337 adds r3,r3
338 orrs r3,r0
339 orrs r3,r2
340 beq 7b
341 mvns r1, r1     @ carry inverse of r1 sign
342 adds r1, r1
343 pop {r0-r7,r15}
344
345
346// int FUNC_NAME(__aeabi_dcmpeq)(double, double)         result (1, 0) denotes (=, ?<>) [2], use for C == and !=
347double_wrapper_section __aeabi_dcmpeq
348.align 2
349wrapper_func __aeabi_dcmpeq
350    push {lr}
351    bl __aeabi_cdcmpeq
352    beq 1f
353    movs r0, #0
354    pop {pc}
3551:
356    movs r0, #1
357    pop {pc}
358
359// int FUNC_NAME(__aeabi_dcmplt)(double, double)         result (1, 0) denotes (<, ?>=) [2], use for C <
360double_wrapper_section __aeabi_dcmplt
361.align 2
362wrapper_func __aeabi_dcmplt
363    push {lr}
364    bl __aeabi_cdcmple
365    sbcs r0, r0
366    pop {pc}
367
368// int FUNC_NAME(__aeabi_dcmple)(double, double)         result (1, 0) denotes (<=, ?>) [2], use for C <=
369double_wrapper_section __aeabi_dcmple
370.align 2
371wrapper_func __aeabi_dcmple
372    push {lr}
373    bl __aeabi_cdcmple
374    bls 1f
375    movs r0, #0
376    pop {pc}
3771:
378    movs r0, #1
379    pop {pc}
380
381// int FUNC_NAME(__aeabi_dcmpge)(double, double)         result (1, 0) denotes (>=, ?<) [2], use for C >=
382double_wrapper_section __aeabi_dcmpge
383.align 2
384wrapper_func __aeabi_dcmpge
385    push {lr}
386    // because of NaNs it is better to reverse the args than the result
387    bl __aeabi_cdrcmple
388    bls 1f
389    movs r0, #0
390    pop {pc}
3911:
392    movs r0, #1
393    pop {pc}
394
395// int FUNC_NAME(__aeabi_dcmpgt)(double, double)         result (1, 0) denotes (>, ?<=) [2], use for C >
396double_wrapper_section __aeabi_dcmpgt
397wrapper_func __aeabi_dcmpgt
398    push {lr}
399    // because of NaNs it is better to reverse the args than the result
400    bl __aeabi_cdrcmple
401    sbcs r0, r0
402    pop {pc}
403
404// int FUNC_NAME(__aeabi_dcmpun)(double, double)         result (1, 0) denotes (?, <=>) [2], use for C99 isunordered()
405double_wrapper_section __aeabi_dcmpun
406wrapper_func __aeabi_dcmpun
407   movs r0, #1
408   lsls r0, #21
409   lsls r2, r1, #1
410   adds r2, r0
411   bhi 1f
412   lsls r2, r3, #1
413   adds r2, r0
414   bhi 1f
415   movs r0, #0
416   bx lr
4171:
418   movs r0, #1
419   bx lr
420
421// double FUNC_NAME(__aeabi_ui2d)(unsigned)             unsigned to double (double precision) conversion
422double_wrapper_section __aeabi_ui2d
423    shimmable_table_tail_call SF_TABLE_UINT2FLOAT uint2double_shim
424
425double_wrapper_section __aeabi_i2d
426
427wrapper_func __aeabi_ui2d
428    movs r1, #0
429    cmp r0, #0
430    bne 2f
4311:
432    bx lr
433// double FUNC_NAME(__aeabi_i2d)(int)                     integer to double (double precision) conversion
434wrapper_func __aeabi_i2d
435    asrs r1, r0, #31
436    eors r0, r1
437    subs r0, r1
438    beq 1b
439    lsls r1, #31
4402:
441    push {r0, r1, r4, lr}
442    ldr r3, =sf_clz_func
443    ldr r3, [r3]
444    blx r3
445    pop {r2, r3}
446    adds r4, r0, #1
447    lsls r2, r4
448    lsls r0, r2, #20
449    lsrs r2, #12
450    ldr r1,=1055
451    subs r1, r4
452    lsls r1, #20
453    orrs r1, r3
454    orrs r1, r2
455    pop {r4, pc}
456
457// int FUNC_NAME(__aeabi_d2iz)(double)                     double (double precision) to integer C-style conversion [3]
458double_wrapper_section __aeabi_d2iz
459wrapper_func __aeabi_d2iz
460regular_func double2int_z
461    push {r4, lr}
462    lsls r4, r1, #1
463    lsrs r2, r4, #21
464    movs r3, #0x80
465    adds r2, r3
466    lsls r3, #3
467    subs r2, r3
468    lsls r3, #21
469    cmp r2, #126
470    ble 1f
471    subs r2, #158
472    bge 2f
473    asrs r4, r1, #31
474    lsls r1, #12
475    lsrs r1, #1
476    orrs r1, r3
477    negs r2, r2
478    lsrs r1, r2
479    lsls r4, #1
480    adds r4, #1
481    adds r2, #21
482    cmp r2, #32
483    bge 3f
484    lsrs r0, r2
485    orrs r0, r1
486    muls r0, r4
487    pop {r4, pc}
4881:
489    movs r0, #0
490    pop {r4, pc}
4913:
492    mov r0, r1
493    muls r0, r4
494    pop {r4, pc}
4952:
496    // overflow
497    lsrs r0, r1, #31
498    adds r0, r3
499    subs r0, #1
500    pop {r4, pc}
501
502double_section double2int
503regular_func double2int
504    shimmable_table_tail_call SF_TABLE_FLOAT2INT double2int_shim
505
506// unsigned FUNC_NAME(__aeabi_d2uiz)(double)             double (double precision) to unsigned C-style conversion [3]
507double_wrapper_section __aeabi_d2uiz
508wrapper_func __aeabi_d2uiz
509regular_func double2uint
510    shimmable_table_tail_call SF_TABLE_FLOAT2UINT double2uint_shim
511
512double_section fix2double
513regular_func fix2double
514    shimmable_table_tail_call SF_TABLE_FIX2FLOAT fix2double_shim
515
516double_section ufix2double
517regular_func ufix2double
518    shimmable_table_tail_call SF_TABLE_UFIX2FLOAT ufix2double_shim
519
520double_section fix642double
521regular_func fix642double
522    shimmable_table_tail_call SF_TABLE_FIX642FLOAT fix642double_shim
523
524double_section ufix2double
525regular_func ufix642double
526    shimmable_table_tail_call SF_TABLE_UFIX642FLOAT ufix642double_shim
527
528// double FUNC_NAME(__aeabi_l2d)(long long)             long long to double (double precision) conversion
529double_wrapper_section __aeabi_l2d
530wrapper_func __aeabi_l2d
531    shimmable_table_tail_call SF_TABLE_INT642FLOAT int642double_shim
532
533// double FUNC_NAME(__aeabi_l2f)(long long)             long long to double (double precision) conversion
534double_wrapper_section __aeabi_ul2d
535wrapper_func __aeabi_ul2d
536    shimmable_table_tail_call SF_TABLE_UINT642FLOAT uint642double_shim
537
538// long long FUNC_NAME(__aeabi_d2lz)(double)             double (double precision) to long long C-style conversion [3]
539double_wrapper_section __aeabi_d2lz
540wrapper_func __aeabi_d2lz
541regular_func double2int64_z
542    cmn r1, r1
543    bcc double2int64
544    push {lr}
545    lsls r1, #1
546    lsrs r1, #1
547    movs r2, #0
548    bl double2ufix64
549    cmp r1, #0
550    bmi 1f
551    movs r2, #0
552    negs r0, r0
553    sbcs r2, r1
554    mov r1, r2
555    pop {pc}
5561:
557    movs r1, #128
558    lsls r1, #24
559    movs r0, #0
560    pop {pc}
561
562double_section double2int64
563regular_func double2int64
564    shimmable_table_tail_call SF_TABLE_FLOAT2INT64 double2int64_shim
565
566// unsigned long long FUNC_NAME(__aeabi_d2ulz)(double)     double to unsigned long long C-style conversion [3]
567double_wrapper_section __aeabi_d2ulz
568wrapper_func __aeabi_d2ulz
569    shimmable_table_tail_call SF_TABLE_FLOAT2UINT64 double2uint64_shim
570
571double_section double2fix64
572regular_func double2fix64
573    shimmable_table_tail_call SF_TABLE_FLOAT2FIX64 double2fix64_shim
574
575double_section double2ufix64
576regular_func double2ufix64
577    shimmable_table_tail_call SF_TABLE_FLOAT2UFIX64 double2ufix64_shim
578
579double_section double2fix
580regular_func double2fix
581    shimmable_table_tail_call SF_TABLE_FLOAT2FIX double2fix_shim
582
583double_section double2ufix
584regular_func double2ufix
585    shimmable_table_tail_call SF_TABLE_FLOAT2UFIX double2ufix_shim
586
587double_wrapper_section __aeabi_d2f
5881:
589#if PICO_DOUBLE_PROPAGATE_NANS
590    // copy sign bit and 23 NAN id bits into sign bit and significant id bits, also set high id bit
591
592    lsrs r0, #30
593    lsls r2, r1, #12
594    lsrs r2, #9
595    asrs r1, #22
596    lsls r1, #22
597    orrs r0, r1
598    orrs r0, r2
599    bx lr
600#endif
601wrapper_func __aeabi_d2f
602#if PICO_DOUBLE_PROPAGATE_NANS
603    movs r3, #1
604    lsls r3, #21
605    lsls r2, r1, #1
606    adds r2, r3
607    bhi 1b
608#endif
609    // note double->float in double table at same index as float->double in double table
610    shimmable_table_tail_call SF_TABLE_FLOAT2DOUBLE double2float_shim
611
612double_wrapper_section srqt
613wrapper_func_d1 sqrt
614    shimmable_table_tail_call SF_TABLE_FSQRT dsqrt_shim
615
616double_wrapper_section sincostan_remainder
617regular_func sincostan_remainder
618    ldr r2, =0x54442D18 // 2 * M_PI
619    ldr r3, =0x401921FB
620    push {lr}
621    // note remainder only uses the divider thru integer divider functions
622    // which save and restore themselves
623    bl remainder
624    pop {pc}
625
626double_wrapper_section cos
627#don't use _d1 as we're doing a range check anyway and infinites/nans are bigger than 1024
628wrapper_func cos
629    // rom version only works for -1024 < angle < 1024
630    lsls r2, r1, #2
631    bcc 1f
632    lsrs r2, #22
633    cmp r2, #9
634    bge 2f
6351:
636    shimmable_table_tail_call SF_TABLE_FCOS dcos_shim
6372:
638#if PICO_DOUBLE_PROPAGATE_NANS
639    lsls r2, r1, #1
640    asrs r2, #21
641    adds r2, #1
642    bne 3f
643    // infinite to nan
644    movs r2, #1
645    lsls r2, #19
646    orrs r1, r2
647    bx lr
6483:
649#endif
650    push {lr}
651    bl sincostan_remainder
652    pop {r2}
653    mov lr, r2
654    b 1b
655
656double_wrapper_section sin
657#don't use _d1 as we're doing a range check anyway and infinites/nans are bigger than 1024
658wrapper_func sin
659    // rom version only works for -1024 < angle < 1024
660    lsls r2, r1, #2
661    bcc 1f
662    lsrs r2, #22
663    cmp r2, #9
664    bge 2f
6651:
666    shimmable_table_tail_call SF_TABLE_FSIN dsin_shim
6672:
668#if PICO_DOUBLE_PROPAGATE_NANS
669    lsls r2, r1, #1
670    asrs r2, #21
671    adds r2, #1
672    bne 3f
673    // infinite to nan
674    movs r2, #1
675    lsls r2, #19
676    orrs r1, r2
677    bx lr
6783:
679#endif
680    push {lr}
681    bl sincostan_remainder
682    pop {r2}
683    mov lr, r2
684    b 1b
685
686double_wrapper_section sincos
687    // out of line remainder code for abs(angle)>=1024
6882:
689#if PICO_DOUBLE_PROPAGATE_NANS
690    lsls r2, r1, #1
691    asrs r2, #21
692    adds r2, #1
693    bne 3f
694    // infinite to nan
695    movs r2, #1
696    lsls r2, #19
697    orrs r1, r2
698    pop {r4-r5}
699    stmia r4!, {r0, r1}
700    stmia r5!, {r0, r1}
701    pop {r4, r5, pc}
7023:
703#endif
704    push {lr}
705    bl sincostan_remainder
706    pop {r2}
707    mov lr, r2
708    b 1f // continue with sincos
709
710wrapper_func sincos
711    push {r2-r5, lr}
712    // rom version only works for -1024 < angle < 1024
713    lsls r2, r1, #2
714    bcc 1f
715    lsrs r2, #22
716    cmp r2, #9
717    bge 2b
7181:
719    bl 2f // call the shim
720    pop {r4-r5}
721    stmia r4!, {r0, r1}
722    stmia r5!, {r2, r3}
723    pop {r4, r5, pc}
724
7252:
726    shimmable_table_tail_call SF_TABLE_V3_FSINCOS sincos_shim_bootstrap
727
728.thumb_func
729sincos_shim_bootstrap:
730    push {r2, r3, r4}
731    movs r3, #0x13
732    ldrb r3, [r3]
733#if PICO_DOUBLE_SUPPORT_ROM_V1 && PICO_RP2040_B0_SUPPORTED
734    cmp r3, #1
735    bne 1f
736    ldr r3, =dsincos_shim
737    b 2f
738#endif
7391:
740    ldr r3, =dsincos_shim_v2
7412:
742    ldr r2, =sd_table
743    str r3, [r2, #SF_TABLE_V3_FSINCOS]
744    str r3, [sp, #8]
745    pop {r2, r3, pc}
746.thumb_func
747dsincos_shim_v2:
748     push {r4-r7,r14}
749     bl push_r8_r11
750     bl v2_rom_dsincos_internal
751     mov r12,r0                    @ save ε
752     bl v2_rom_dcos_finish
753     push {r0,r1}
754     mov r0,r12
755     bl v2_rom_dsin_finish
756     pop {r2,r3}
757     bl pop_r8_r11
758     pop {r4-r7,r15}
759.thumb_func
760v2_rom_dsincos_internal:
761    push {r0, lr}
762    ldr r0, =0x3855
763    str r0, [sp, #4]
764    pop {r0, pc}
765.thumb_func
766v2_rom_dcos_finish:
767    push {r0, r1}
768    ldr r0, =0x389d
769    str r0, [sp, #4]
770    pop {r0, pc}
771.thumb_func
772v2_rom_dsin_finish:
773    push {r0, r1}
774    ldr r0, =0x38d9
775    str r0, [sp, #4]
776    pop {r0, pc}
777
778double_wrapper_section tan
779#don't use _d1 as we're doing a range check anyway and infinites/nans are bigger than 1024
780wrapper_func tan
781    // rom version only works for -1024 < angle < 1024
782    lsls r2, r1, #2
783    bcc dtan_in_range
784    lsrs r2, #22
785    cmp r2, #9
786    bge dtan_angle_out_of_range
787dtan_in_range:
788#if !PICO_DIVIDER_DISABLE_INTERRUPTS
789    // to support IRQ usage (or context switch) we must save/restore divider state around call if state is dirty
790    mov ip, r2
791    ldr r2, =(SIO_BASE)
792    ldr r2, [r2, #SIO_DIV_CSR_OFFSET]
793    lsrs r2, #SIO_DIV_CSR_DIRTY_SHIFT_FOR_CARRY
794    bcs dtan_save_state
795    mov r2, ip
796#else
797    // to avoid worrying about IRQs (or context switches), simply disable interrupts around call
798    push {r4, lr}
799    mrs r4, PRIMASK
800    cpsid i
801    bl dtan_shim_call
802    msr PRIMASK, r4
803    pop {r4, pc}
804#endif
805dtan_shim_call:
806    shimmable_table_tail_call SF_TABLE_FTAN dtan_shim
807#if !PICO_DIVIDER_DISABLE_INTERRUPTS
808dtan_save_state:
809    ldr r2, =(SIO_BASE)
810    save_div_state_and_lr
811    mov r2, ip
812    bl dtan_shim_call
813    ldr r2, =(SIO_BASE)
814    restore_div_state_and_return
815#endif
816dtan_angle_out_of_range:
817#if PICO_DOUBLE_PROPAGATE_NANS
818    lsls r2, r1, #1
819    asrs r2, #21
820    adds r2, #1
821    bne 3f
822    // infinite to nan
823    movs r2, #1
824    lsls r2, #19
825    orrs r1, r2
826    bx lr
8273:
828#endif
829    push {lr}
830    bl sincostan_remainder
831    pop {r2}
832    mov lr, r2
833    b dtan_in_range
834
835double_wrapper_section atan2
836wrapper_func_d2 atan2
837    shimmable_table_tail_call SF_TABLE_FATAN2 datan2_shim
838
839double_wrapper_section exp
840wrapper_func_d1 exp
841    shimmable_table_tail_call SF_TABLE_FEXP dexp_shim
842
843double_wrapper_section log
844wrapper_func_d1 log
845    shimmable_table_tail_call SF_TABLE_FLN dln_shim
846
847#endif