; Copyright 1996 Acorn Computers Ltd ; ; Licensed under the Apache License, Version 2.0 (the "License"); ; you may not use this file except in compliance with the License. ; You may obtain a copy of the License at ; ; http://www.apache.org/licenses/LICENSE-2.0 ; ; Unless required by applicable law or agreed to in writing, software ; distributed under the License is distributed on an "AS IS" BASIS, ; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ; See the License for the specific language governing permissions and ; limitations under the License. ; ; -*- Mode: Assembler -*- ;* Lastedit: 27 Mar 90 12:50:15 by Harry Meekings * ; body of shared C library (to sit on top of shared library kernel) ; ; Copyright (C) Acorn Computers Ltd., 1988. ; ; 23-Sep-94 AMcC __rt_ symbols defined and exported (compatible with cc vsn 5 etc) ; GET s.h_Stack GET Hdr:Wimp GET Hdr:TaskWindow GBLL FloatingPointArgsInRegs FloatingPointArgsInRegs SETL {FALSE} [ FloatingPointArgsInRegs ! 0, "WARNING: Floating point arguments ARE being passed in FP registers" ] SIGFPE * 2 SIGILL * 3 SIGINT * 4 SIGSEGV * 5 SIGSTAK * 7 SIGOSERROR * 10 ERANGE * 2 EDOM * 1 EXPORT TrapHandler EXPORT UncaughtTrapHandler EXPORT EventHandler EXPORT UnhandledEventHandler MACRO $Label DisableFPInterrupts ; Disables all FP exceptions, remembering the exception mask in ip ; for subsequent reinstatement by ReEnableFPInterrupts. (ip must ; therefore be left alone by the FP procedures which call this macro). $Label MOV r1, #0 RFS ip WFS r1 MEND MACRO $Label ReEnableFPInterrupts ; Reinstates the exception mask state which prevailed before the call ; to DisableFPInterrupts; sets r1 to the current fp flags. $Label RFS r1 WFS ip MEND IMPORT |_signal_real_handler| IMPORT |_armsys_lib_init| IMPORT |_sys_msg| IMPORT |_sys_msg_1| IMPORT |_kernel_fpavailable| IMPORT |_kernel_exittraphandler| IMPORT |_kernel_setreturncode| IMPORT |_kernel_exit| IMPORT |_kernel_osbyte| IMPORT |_kernel_osrdch| IMPORT |_kernel_oswrch| IMPORT |_kernel_osbget| IMPORT |_kernel_osbput| IMPORT |_kernel_osgbpb| IMPORT |_kernel_osword| IMPORT |_kernel_osfind| IMPORT |_kernel_osfile| IMPORT |_kernel_osargs| IMPORT |_kernel_oscli| IMPORT |_kernel_last_oserror| IMPORT |_kernel_system| IMPORT |_kernel_raise_error| IMPORT |_kernel_udiv| IMPORT |_kernel_urem| IMPORT |_kernel_sdiv| IMPORT |_kernel_srem| IMPORT |_kernel_stkovf_split_0frame| IMPORT |_kernel_stkovf_split| IMPORT |_kernel_getmessage| EXPORT |_clib_initialise| EXPORT |x$stack_overflow| EXPORT |x$stack_overflow_1| EXPORT |_raise_stacked_interrupts| EXPORT |x$divtest| EXPORT |x$multiply| EXPORT |_rd1chk| EXPORT |_rd2chk| EXPORT |_rd4chk| EXPORT |_wr1chk| EXPORT |_wr2chk| EXPORT |_wr4chk| EXPORT |_memcpy| EXPORT |_memset| EXPORT |_postmortem| EXPORT |setjmp| EXPORT |longjmp| EXPORT |_exit| EXPORT |_oswrch| EXPORT |_osbget| EXPORT |_osbput| EXPORT |_osgbpb| EXPORT |_osgbpb1| EXPORT |_osrdch| EXPORT |_osfind| EXPORT |_osword| EXPORT |_osfile| EXPORT |_osfile1| EXPORT |_osargs| EXPORT |_oscli| EXPORT |_osbyte| EXPORT |_count| EXPORT |_count1| EXPORT |_default_sigstak_handler| EXPORT |_SignalNumber| EXPORT |_ldfp| EXPORT |_stfp| EXPORT |__fpclassifyf| EXPORT |__fpclassifyd| EXPORT |__signbitf| EXPORT |__signbitd| EXPORT copysign EXPORT copysignf EXPORT nextafter EXPORT nextafterf EXPORT |__rt_stkovf_split_small| EXPORT |__rt_stkovf_split_big| EXPORT |__rt_divtest| EXPORT |__rt_rd1chk| EXPORT |__rt_rd2chk| EXPORT |__rt_rd4chk| EXPORT |__rt_wr1chk| EXPORT |__rt_wr2chk| EXPORT |__rt_wr4chk| EXPORT fmin EXPORT fminf EXPORT fmax EXPORT fmaxf EXPORT sin EXPORT cos EXPORT exp EXPORT log10 EXPORT log EXPORT sqrt EXPORT tan EXPORT atan EXPORT asin EXPORT acos EXPORT pow GET clib.s.h_signal |_clib_initialise| FunctionEntry "r1,r2" LoadStaticBase ip, a4 MOV r0, #-1 ; read max app space size SWI XOS_ReadDynamicArea ADDVC r2, r2, r0 ; if call works then get end of app space MOVVS r2, #&01000000 ; if call fails then assume 16M STR r2, [ip, #O_app_space_end] MOV a4, #0 STRB a4, [ip, #O_inSignalHandler] BL |_armsys_lib_init| MOV a1, #0 Return "r1,r2" [ ModeMayBeNonUser IMPORT |_lib_shutdown| EXPORT |_clib_finalisemodule| |_clib_finalisemodule| FunctionEntry "a1" BL |_lib_shutdown| MOV a1, #0 BL |_exit| LDMFD sp!, {r1} ; free the RMA block addressed by MOV r0, #Module_Free ; our private word. LDR r2, [r1] MOV r3, #0 STR r3, [r1] SWI Module [ {CONFIG}<>26 CLRV VS ; explicitly clear V for 32-bit call case ] Return ] EventHandler Keep CMP a1, #-1 MOVNE a1, #0 ; not interested in events other than escape Return ,LinkNotStacked, NE STMFD sp!, {a2, r14} MOV a1, #SIGINT BL |_signal_real_handler| LDMFD sp!, {a2, r14} CMP a1, #0 Return ,LinkNotStacked, EQ LoadStaticBase ip, a1 LDR a1, [ip, #O__interrupts_off] CMP a1, #0 MOV a1, #SIGINT STRNE a1, [ip, #O__saved_interrupt] BNE %FT01 STMFD sp!, {r14} BL raise LDMFD sp!, {r14} 01 MOV a1, #1 ; we wish to handle it, but not just yet Return ,LinkNotStacked UnhandledEventHandler Keep CMP a1, #-1 MOVNE a1, #0 ; not interested in events other than escape Return ,LinkNotStacked, NE LoadStaticBase ip, a1 LDR a1, [ip, #O__interrupts_off] CMP a1, #0 MOV a1, #SIGINT STRNE a1, [ip, #O__saved_interrupt] BNE %FT02 STMFD sp!, {r14} BL raise LDMFD sp!, {r14} 02 MOV a1, #1 ; we wish to handle it, but not just yet Return ,LinkNotStacked |_SignalNumber| SignalNumber Keep ; nb required not to disturb a4 MOV a2, #SIGOSERROR CMP a1, #Error_IllegalInstruction CMPNE a1, #Error_PrefetchAbort CMPNE a1, #Error_BranchThroughZero MOVEQ a2, #SIGILL CMP a1, #Error_DataAbort CMPNE a1, #Error_AddressException MOVEQ a2, #SIGSEGV LDR a3, =Error_FPBase CMP a1, a3 ADD a3, a3, #Error_FPLimit-Error_FPBase-1 CMPHS a3, a1 MOVHS a2, #SIGFPE CMP a1, #Error_DivideByZero MOVEQ a2, #SIGFPE CMP a1, #Error_StackOverflow MOVEQ a2, #SIGSTAK LDR a3, =Error_ReadFail SUBS a3, a1, a3 CMPNE a3, #Error_WriteFail-Error_ReadFail MOVEQ a2, #SIGSEGV MOV a1, a2 Return ,LinkNotStacked UncaughtTrapHandler Keep STMFD sp!, {a2, r14} MOV a4, a1 BL SignalNumber LDMFD sp!, {a2, r14} B RaiseIt |_raise_stacked_interrupts| ; called by CLIB. LoadStaticBase ip, a1 MOV a2, #0 STR a2, [ip, #O__interrupts_off] LDR a1, [ip, #O__saved_interrupt] CMPS a1, #0 Return ,LinkNotStacked, EQ STR a2, [ip, #O__saved_interrupt] B raise Finalise Keep ; (There'd better be a stack set up). IMPORT |_lib_shutdown| B |_lib_shutdown| TrapHandler Keep MOV a4, a1 STMFD sp!, {a2, a4, r14} BL SignalNumber STMFD sp!, {a1} BL |_signal_real_handler| CMP a1, #0 LDMFD sp!, {a1, a2, a4, r14} MOVEQ a1, #0 Return ,LinkNotStacked, EQ RaiseIt LoadStaticBase ip, a3 MOV a3, #1 STRB a3, [ip, #O_inSignalHandler] STMFD sp!, {a2, a4} LDMIB a2, {a2-v6} BL raise ; raise desired signal Raised B |_postmortem| ; and if user insists on returning from ; signal handler, complain loudly! div ldiv imaxdiv FunctionEntry "a1" MOV a1, a3 BL _kernel_sdiv ; a1 := a2 / a1, a2 := a2 % a1 Pull "ip,lr" STMIA ip, {a1,a2} Return ,LinkNotStacked |x$multiply| ; a1 := a2 * a1. ; a2, a3 can be scrambled. MUL a1, a2, a1 Return ,LinkNotStacked IMPORT |_kernel_copyerror| IMPORT |_kernel_fault| |__rt_divtest| |x$divtest| ; test for division by zero (used when division is voided) TEQS a1, #0 Return ,LinkNotStacked, NE STMFD sp!, {r0} ADR r0, E_DivideByZero B |_kernel_fault| |__rt_rd1chk| |_rd1chk| FunctionEntry CMP a1, #&8000 BLT readfail LoadStaticBase ip, lr LDR lr, [ip, #O_app_space_end] CMP a1, lr ; max app space size now read at initialisation Return ,,CC B readfail |__rt_rd2chk| |_rd2chk| FunctionEntry CMP a1, #&8000 BLT readfail TST a1, #1 BNE readfail LoadStaticBase ip, lr LDR lr, [ip, #O_app_space_end] CMP a1, lr ; max app space size now read at initialisation Return ,,CC B readfail |__rt_rd4chk| |_rd4chk| FunctionEntry CMP a1, #&8000 BLT readfail TST a1, #3 BNE readfail LoadStaticBase ip, lr LDR lr, [ip, #O_app_space_end] CMP a1, lr ; max app space size now read at initialisation Return ,,CC B readfail |__rt_wr1chk| |_wr1chk| FunctionEntry CMP a1, #&8000 BLT writefail LoadStaticBase ip, lr LDR lr, [ip, #O_app_space_end] CMP a1, lr ; max app space size now read at initialisation Return ,,CC B writefail |__rt_wr2chk| |_wr2chk| FunctionEntry CMP a1, #&8000 BLT writefail TST a1, #1 BNE writefail LoadStaticBase ip, lr LDR lr, [ip, #O_app_space_end] CMP a1, lr ; max app space size now read at initialisation Return ,,CC B writefail |__rt_wr4chk| |_wr4chk| FunctionEntry CMP a1, #&8000 BLT writefail TST a1, #3 BNE writefail LoadStaticBase ip, lr LDR lr, [ip, #O_app_space_end] CMP a1, lr ; max app space size now read at initialisation Return ,,CC ; and drop into writefail writefail LDMFD sp!, {lr} STMFD sp!, {r0} ADR r0, E_WriteFail B |_kernel_fault| readfail LDMFD sp!, {lr} STMFD sp!, {r0} ADR r0, E_ReadFail B |_kernel_fault| ErrorBlock DivideByZero, "Divide by zero", C06 ErrorBlock ReadFail, "Illegal read", C07 ErrorBlock WriteFail, "Illegal write", C08 MACRO NOOP & 0 MEND ; Note that the number of instructions is critical for the SUBLT, hence the NOOPs |_memcpy| FunctionEntry "v1" STMFD sp!, {v1, lr} 01 SUBS a3, a3, #16 SUBLT pc, pc, a3, ASL #2 LDMIA a2!, {a4, v1, ip, lr} STMIA a1!, {a4, v1, ip, lr} BGT %B01 Return "v1" NOOP LDMIA a2!, {a4, v1, ip} STMIA a1!, {a4, v1, ip} Return "v1" NOOP LDMIA a2!, {a4, v1} STMIA a1!, {a4, v1} Return "v1" NOOP LDMIA a2!, {a4} STMIA a1!, {a4} Return "v1" ; Note that the number of instructions is critical for the SUBLT |_memset| FunctionEntry MOV a4, a2 MOV ip, a2 MOV lr, a2 01 SUBS a3, a3, #16 SUBLT pc, pc, a3, ASL #1 STMIA a1!, {a2, a4, ip, lr} BGT %B01 Return STMIA a1!, {a2, a4, ip} Return STMIA a1!, {a2, a4} Return STMIA a1!, {a4} Return mesg DCB "mesg" [ :DEF:DEFAULT_TEXT postmortem_title DCB "Postmortem", 0 ] postmortem_tag DCB "C65", 0 ALIGN IMPORT |_desktop_task| IMPORT |_kernel_unwind| IMPORT exit |_postmortem| ; Prepare window manager for output ; First check this is a wimp task STMFD sp!, {r0-r5, r14} [ :DEF:DEFAULT_TEXT ADR r0, postmortem_title ADR r1, postmortem_tag | ADR r0, postmortem_tag ] BL |_kernel_getmessage| MOV r5, r0 postmortem0 LDMFD sp, {a1-a2} LDR a3, mesg ; if magic arg2 != "mesg" CMP a2, a3 ; then go straight onto postmortem BNE postmortem1 MOV a2, r5 ; if so, offer postmortem BL |_sys_msg_1| TEQ r4, #1 TEQ a1, #0 ; if didn't display the button TEQNE a1, #3 ; or if he selected it, then carry on with postmortem MOVNE a1, #1 ; otherwise exit(EXIT_FAILURE) BNE exit postmortem1 BL _desktop_task TEQ a1, #0 BEQ postmortem2 LoadStaticAddress __iob + 2 * 40, a2, a3 ; a2 -> stderr LDR r4, [a2, #20] ; a2 = stderr->__file TEQ r4, #0 ; istty(stderr->__file)? BNE postmortem2 MOV r0, r5 ; if desktop, and stderr = tty SWI XWimp_CommandWindow ; then open command window, SWI XOS_WriteI + 14 ; page mode on postmortem2 LDMFD sp!, {r0-r5} BL |_kernel_fpavailable| LDMFD sp!, {r14} CMP a1, #0 MOV ip, sp SUBEQ sp, sp, #12*4 BEQ postmortem_nofp STFE f7, [sp, #-12]! STFE f6, [sp, #-12]! STFE f5, [sp, #-12]! STFE f4, [sp, #-12]! postmortem_nofp STR sl, [sp, #-4]! STMFD sp!, {v1-v6, fp, ip, r14} ADD a4, sp, #21*4 LDMDB a4!, {v1-v6, ip} STMFD sp!, {v1-v6, ip} LDMDB a4!, {v1-v6, ip} STMFD sp!, {v1-v6, ip} LDMDB a4!, {v1-v6, ip} STMFD sp!, {v1-v6, ip} SUB sp, sp, #4 MOV v1, #4 02 SUBS v1, v1, #1 BLT %F01 ADD a2, sp, #0 ADD a1, sp, #4 BL |_kernel_unwind| CMP a1, #0 BLE %F01 LDR a1, [sp, #4+8*4] RemovePSRFromReg a1, a2 ; Remove PSR bits safely if necessary ADR a2, Raised CMP a1, a2 BNE %B02 LDR a1, [sp, #4+7*4] ADD a2, sp, #4 ADD a3, sp, #4+21*4 LDMIA a3, {v1-v6} STMFD sp!, {v1-v6} LDR a4, [a1] LDMFD a2!, {v1-v6, ip} STMIA a3!, {v1-v6, ip} LDMFD a2!, {v1-v6, ip} LDR r14, [a3, #4] LDR v2, [a4, #pc*4] STMIA a3!, {v1-v6, ip} LDMFD a2!, {v1-v6, ip} STMIA a3!, {v1-v6, ip} LDR a2, [a4, #r0*4] LDR a1, [a1, #4] LDMFD sp!, {v1-v6} B %F02 01 LDR v1, [sp, #4+21*4] LDR r14, [sp, #4+(21+8)*4] MOV a1, #-1 MOV a2, #0 02 ADD sp, sp, #22*4 MOV a3, sp B |_backtrace| ;MOV a1, #&4000 ;MOV a2, #&4000 ;MOV a3, #&20 ;MOV a4, #&FF ;SWI XOS_ReadLine ;MOVCS a1, #124 ;SWICS XOS_Byte ;SWI XWimp_CloseDown ;ADR a1, debug_cmd ;SWI XOS_CLI ;MOVVC a1, #0 ;LDRVS a1, [a1] ;TEQ a1, #0 ;TEQNE a1, #&11 ;MOVEQ a1, #-1 ;SWIEQ XWimp_CommandWindow ;MOV a1, #1 ;B exit debug_cmd DCB "Debug", 0 ALIGN |__rt_stkovf_split_small| |x$stack_overflow| B |_kernel_stkovf_split_0frame| |__rt_stkovf_split_big| |x$stack_overflow_1| B |_kernel_stkovf_split| |_exit| [ No32bitCode TST r14, #3 | MRS a4, CPSR TST a4, #2_01111 ; EQ if USR26 or USR32 ] BLEQ |_kernel_setreturncode| ; BL<cond> 32-bit OK B |_kernel_exit| ^ 0 sj_v1 # 4 sj_v2 # 4 sj_v3 # 4 sj_v4 # 4 sj_v5 # 4 sj_v6 # 4 [ {CONFIG}=26 sj_fp # 4 ; Old APCS-A ordering, which we retain sj_sp # 4 ; for compatibility in the 26-bit case sj_sl # 4 ; (someone might poke jmp_bufs) | sj_sl # 4 sj_fp # 4 sj_sp # 4 ] sj_pc # 4 sj_f4 # 3*4 sj_f5 # 3*4 sj_f6 # 3*4 sj_f7 # 3*4 |setjmp| ; save everything that might count as a register variable value. [ {CONFIG}=26 STMIA a1!, {v1-v6, fp, sp} STMIA a1, {sl, lr} SUB v1, a1, #sj_sl | STMIA a1, {v1-v6, sl, fp, sp, lr} MOV v1, a1 ] MOV v2, lr BL |_kernel_fpavailable| CMP a1, #0 [ {CONFIG}=26 STFNEE f4, [v1, #sj_f4] STFNEE f5, [v1, #sj_f5] STFNEE f6, [v1, #sj_f6] STFNEE f7, [v1, #sj_f7] | SFMNE f4, 4, [v1, #sj_f4] ] MOV a1, #0 MOV lr, v2 LDMIA v1, {v1, v2} Return ,LinkNotStacked |longjmp| ADD v1, a1, #sj_f4 MOVS v6, a2 MOVEQ v6, #1 ; result of setjmp == 1 on longjmp(env, 0) LoadStaticBase ip, a1 LDRB a1, [ip, #O_inSignalHandler] CMP a1, #0 MOVNE a1, #0 STRNEB a1, [ip, #O_inSignalHandler] BLNE |_kernel_exittraphandler| ; BL<cond> 32-bit OK BL |_kernel_fpavailable| CMP a1, #0 [ {CONFIG}=26 LDFNEE f7, [v1, #sj_f7-sj_f4] LDFNEE f6, [v1, #sj_f6-sj_f4] LDFNEE f5, [v1, #sj_f5-sj_f4] LDFNEE f4, [v1, #sj_f4-sj_f4] LDMDB v1!, {v2-v5} ; fudge for atomic {sp,sl,fp} update STMFD sp!, {v2, v3} ; push fp, sp STR v4, [sp, #-4]! ; push sl LDMFD sp, {sl, fp, sp} | LFMNE f4, 4, [v1, #sj_f4-sj_f4] LDMDB v1!, {sl, fp, sp, lr} MOV v5, lr ] [ ModeMayBeNonUser [ {CONFIG}=26 MOV a1, pc TST a1, #3 | MRS a1, CPSR TST a1, #2_01111 ; EQ if USR26 or USR32 ] BNE chunks_deallocated ] ; Now discard all unwanted stack chunks which are deallocatable. LDR v2, [sl, #SC_next-SC_SLOffset] ; first extension chunk ADD v4, sl, #SC_next-SC_SLOffset ; base of extension chain CMP v2, #0 BEQ chunks_deallocated deallocate_chunks LDR ip, [v2, #SC_deallocate] LDR v3, [v2, #SC_next] ; chunk after next CMPS ip, #0 ADDEQ v4, v2, #SC_next ; if it can't be deallocated BEQ deallocate_next_chunk ; retain it MOV a1, v2 MOV lr, pc ;) deallocate it if it can be MOV pc, ip ;) deallocated, and update the STR v3, [v4] ;) chain. deallocate_next_chunk MOVS v2, v3 BNE deallocate_chunks chunks_deallocated MOV lr, v5 MOV a1, v6 LDMDB v1, {v1-v6} Return ,LinkNotStacked |_oswrch| B |_kernel_oswrch| |_osbget| B |_kernel_osbget| |_osbput| B |_kernel_osbput| |_osgbpb| STMFD sp!, {a3, a4, v1, v2, v3} ; v1-v3 just to reserve space STMFD sp!, {r14} ADD a3, sp, #4 BL |_kernel_osgbpb| CMP a1, #-2 LDRNE a1, [sp, #8] ; new value of len LDMFD sp!, {r14} ADD sp, sp, #5*4 Return ,LinkNotStacked |_osgbpb1| B |_kernel_osgbpb| |_osrdch| B |_kernel_osrdch| |_osword| B |_kernel_osword| |_osfind| B |_kernel_osfind| |_osfile| STMFD sp!, {a3, a4, v1, v2} ; v1,v2 just to reserve space STMFD sp!, {r14} ADD a3, sp, #4 BL |_kernel_osfile| LDMFD sp!, {r14} ADD sp, sp, #4*4 Return ,LinkNotStacked |_osfile1| B |_kernel_osfile| |_osargs| B |_kernel_osargs| |_oscli| B |_kernel_oscli| |_osbyte| B |_kernel_osbyte| ; double _ldfp(void *x) converts packed decimal at x to a double |_ldfp| DisableFPInterrupts LDFP f0, [r0, #0] ADFD f0, f0, #0 ; (round to D format) ReEnableFPInterrupts TST r1, #&F Return ,LinkNotStacked, EQ TST r1, #&7 BNE ldfp_overflow B underflow_error ; void _stfp(double d, void *x) stores packed decimal at x |_stfp| [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} LDFD f0, [sp], #8 STFP f0, [r2, #0] | STFP f0, [r0, #0] ] Return ,LinkNotStacked FP_ZERO * 0 FP_SUBNORMAL * 1 FP_NORMAL * 2 FP_INFINITY * 3 FP_NAN * 4 ; These functions always take arguments in registers, to prevent ; signalling NaN problems. The wrapper macros ensure they are ; passed the correct type, so the initial store can't cause an ; exception. |__fpclassifyf| STFS f0, [sp, #-4]! LDR r0, [sp], #4 BICS r0, r0, #&80000000 ; ignore sign; EQ if Exp and M == 0 (and r0 == 0 == FP_ZERO) ASSERT FP_ZERO = 0 Return ,LinkNotStacked,EQ MOVS r2, r0, LSR #23 ; R2 := Exp; EQ if Exp == 0 MOVEQ r0, #FP_SUBNORMAL Return ,LinkNotStacked,EQ TEQ r2, #&ff MOVNE r0, #FP_NORMAL Return ,LinkNotStacked,NE MOVS r2, r0, LSL #9 ; R2 := M; EQ if M == 0 MOVEQ r0, #FP_INFINITY MOVNE r0, #FP_NAN Return ,LinkNotStacked |__fpclassifyd| STFD f0, [sp, #-8]! LDMIA sp!, {r0, r1} BICS r0, r0, #&80000000 ; ignore sign; EQ if Exp and MHi == 0 TEQEQS r1, #0 ; EQ if Exp and M == 0 (and r0 == 0 == FP_ZERO) ASSERT FP_ZERO = 0 Return ,LinkNotStacked,EQ MOVS r2, r0, LSR #20 ; R2 := Exp; EQ if Exp == 0 MOVEQ r0, #FP_SUBNORMAL Return ,LinkNotStacked,EQ ADDS r0, r0, #&00100000 ; If Exp = &7FF then R0 := &800xxxxx, so MI MOVPL r0, #FP_NORMAL Return ,LinkNotStacked,PL ORRS r0, r1, r0, LSL #1 ; Look for any non-zero bits (except top bit of R0) MOVEQ r0, #FP_INFINITY MOVNE r0, #FP_NAN Return ,LinkNotStacked |__signbitf| STFS f0, [sp, #-4]! LDR r0, [sp], #4 MOV r0, r0, LSR #31 Return ,LinkNotStacked |__signbitd| STFD f0, [sp, #-8]! LDR r0, [sp], #8 MOV r0, r0, LSR #31 Return ,LinkNotStacked copysign STFD f1, [sp, #-8]! LDR r2, [sp], #8 TEQ r2, #0 ; MI if y negative ABSD f0, f0 MNFMID f0, f0 Return ,LinkNotStacked copysignf STFS f1, [sp, #-4]! LDR r1, [sp], #4 TEQ r1, #0 ; MI if y negative ABSS f0, f0 MNFMIS f0, f0 Return ,LinkNotStacked ; Back to normal calling conventions nextafter [ :LNOT:FloatingPointArgsInRegs STMFD sp!, {r0-r3} LDFD f0, [sp], #8 LDFD f1, [sp], #8 ] CMF f1, f0 BEQ copysign BGT nextup BMI nextdown ; unordered ADFD f0, f0, f1 ; do the NaN propagation + exceptions Return ,LinkNotStacked nextup [ FloatingPointArgsInRegs STFD f0, [sp, #-8]! LDMFD sp!, {r0, r1} ] TEQ r0, #0 BPL nextbigger BMI nextsmaller_neg nextdown [ FloatingPointArgsInRegs STFD f0, [sp, #-8]! LDMFD sp!, {r0, r1} ] TEQ r0, #0 BPL nextsmaller_pos BMI nextbigger nextbigger CMN r0, #&00100000 Return ,LinkNotStacked,CS ; catch infinity (NaN already done) Return ,LinkNotStacked,VS nextbigger_finite ; For all finite cases (both +ve and -ve), it's just a 64-bit +1: ; ; 00000000 00000000 -> 00000000 00000001 0 => smallest subnormal ; 000FFFFF FFFFFFFF -> 00100000 00000000 largest subnormal => smallest normal ; 7FEFFFFF FFFFFFFF -> 7FF00000 00000000 largest normal => infinity ADDS r1, r1, #1 ADC r0, r0, #0 retval STMFD sp!, {r0, r1} LDFD f0, [sp], #8 Return ,LinkNotStacked nextsmaller_pos ; 7FF00000 00000000 -> 7FEFFFFF FFFFFFFF +infinity => largest +ve normal ; 00100000 00000000 -> 000FFFFF FFFFFFFF smallest +ve normal => largest +ve subnormal ; 00000000 00000001 -> 00000000 00000000 smallest +ve subnormal => +0 ; 00000000 00000000 -> FFFFFFFF FFFFFFFF clears carry, so we can catch +0 => -ve case SUBS r1, r1, #1 SBCS r0, r0, #0 STMCSFD sp!, {r0, r1} LDFCSD f0, [sp], #8 LDFCCD f0, tiny_neg ; -0 => smallest +ve subnormal Return ,LinkNotStacked nextsmaller_neg ; FFF00000 00000000 -> FFEFFFFF FFFFFFFF -infinity => largest -ve normal ; 80100000 00000000 -> 800FFFFF FFFFFFFF smallest -ve normal => largest -ve subnormal ; 80000000 00000001 -> 80000000 00000000 smallest -ve subnormal => -0 ; 80000000 00000000 -> 7FFFFFFF FFFFFFFF sets overflow, so we can catch -0 => +ve case SUBS r1, r1, #1 SBCS r0, r0, #0 STMVCFD sp!, {r0, r1} LDFVCD f0, [sp], #8 LDFVSD f0, tiny_pos ; -0 => smallest +ve subnormal Return ,LinkNotStacked nexttowardf [ :LNOT:FloatingPointArgsInRegs STMFD sp!, {r0-r3} LDFD f0, [sp], #8 LDFD f1, [sp], #8 ] MVFS f0, f0 CMF f1, f0 BEQ copysignf BGT nextupf BMI nextdownf ; unordered ADFS f0, f0, f1 ; do the NaN propagation + exceptions Return ,LinkNotStacked nextafterf [ :LNOT:FloatingPointArgsInRegs STMFD sp!, {r0-r3} LDFD f0, [sp], #8 LDFD f1, [sp], #8 ] MVFS f0, f0 MVFS f1, f1 CMF f1, f0 BEQ copysignf BGT nextupf BMI nextdownf ; unordered ADFS f0, f0, f1 ; do the NaN propagation + exceptions Return ,LinkNotStacked nextupf STFS f0, [sp, #-4]! LDR r0, [sp], #4 TEQ r0, #0 BPL nextbiggerf BMI nextsmallerf_neg nextdownf STFS f0, [sp, #-4]! LDR r0, [sp], #4 TEQ r0, #0 BPL nextsmallerf_pos BMI nextbiggerf nextbiggerf ADD r1, r0, #&00800000 ; spot infinities - NaNs already TEQ r0, r1 ; caught in nexttowardf. Return ; them unchanged (MI if +/-inf) ; For all finite cases (both +ve and -ve), it's just a 32-bit +1: ; ; 00000000 -> 00000001 0 => smallest subnormal ; 007FFFFF -> 00800000 largest subnormal => smallest normal ; 7F7FFFFF -> 7F800000 largest normal => infinity ADDPL r0, r0, #1 STRPL r0, [sp, #-4]! LDFPLS f0, [sp], #4 Return ,LinkNotStacked nextsmallerf_pos ; 7F800000 -> 7F7FFFFF +infinity => largest +ve normal ; 00800000 -> 007FFFFF smallest +ve normal => largest +ve subnormal ; 00000001 -> 00000000 smallest +ve subnormal => +0 ; 00000000 -> FFFFFFFF clears carry, so we can catch +0 => -ve case SUBS r0, r0, #1 STRCS r0, [sp, #-4]! LDFCSS f0, [sp], #4 LDFCCS f0, tiny_negf ; -0 => smallest +ve subnormal Return ,LinkNotStacked nextsmallerf_neg ; FF800000 -> FF7FFFFF -infinity => largest -ve normal ; 80800000 -> 807FFFFF smallest -ve normal => largest -ve subnormal ; 80000001 -> 80000000 smallest -ve subnormal => -0 ; 80000000 -> 7FFFFFFF sets overflow, so we can catch -0 => +ve case SUBS r0, r0, #1 STRVC r0, [sp, #-4]! LDFVCS f0, [sp], #4 LDFVSS f0, tiny_posf ; -0 => smallest +ve subnormal Return ,LinkNotStacked tiny_neg DCD &80000000 DCD &00000001 tiny_pos DCD &00000000 tiny_posf DCD &00000001 tiny_negf DCD &80000001 fmax [ :LNOT:FloatingPointArgsInRegs STMFD sp!, {r0-r3} LDFD f0, [sp], #8 LDFD f1, [sp], #8 ] CMF f1, f0 MVFGTD f0, f1 Return ,LinkNotStacked,VC fcmpnan CMF f0, #0 MVFVSD f0, f1 Return ,LinkNotStacked fmaxf [ :LNOT:FloatingPointArgsInRegs STMFD sp!, {r0-r3} LDFD f0, [sp], #8 LDFD f1, [sp], #8 ] MVFS f0, f0 MVFS f1, f1 CMF f1, f0 MVFGTS f0, f1 Return ,LinkNotStacked,VC B fcmpnan fmin [ :LNOT:FloatingPointArgsInRegs STMFD sp!, {r0-r3} LDFD f0, [sp], #8 LDFD f1, [sp], #8 ] CMF f1, f0 MVFMID f0, f1 Return ,LinkNotStacked,VC B fcmpnan fminf [ :LNOT:FloatingPointArgsInRegs STMFD sp!, {r0-r3} LDFD f0, [sp], #8 LDFD f1, [sp], #8 ] MVFS f0, f0 MVFS f1, f1 CMF f1, f0 MVFMIS f0, f1 Return ,LinkNotStacked,VC B fcmpnan [ {FALSE} fmaf [ :LNOT:FloatingPointArgsInRegs STMFD sp!, {r0-r3} LDFD f0, [sp], #8 LDFD f1, [sp], #8 ] LDFD f2, [sp, #0] MVFS f0, f0 MVFS f1, f1 MVFS f2, f2 MUFE f0, f0, f1 ; totally accurate result ADFS f0, f0, f2 Return ,LinkNotStacked ] sin [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} LDFD f0, [sp], #8 ] SIND f0, f0 Return ,LinkNotStacked [ {FALSE} sinf [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} LDFD f0, [sp], #8 ] MVFS f0, f0 SINS f0, f0 Return ,LinkNotStacked ] cos [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} LDFD f0, [sp], #8 ] COSD f0, f0 Return ,LinkNotStacked [ {FALSE} cosf [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} LDFD f0, [sp], #8 ] MVFS f0, f0 COSS f0, f0 Return ,LinkNotStacked ] exp [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} DisableFPInterrupts LDFD f0, [sp], #8 | DisableFPInterrupts ] EXPD f0, f0 ReEnableFPInterrupts TST r1, #&0F Return ,LinkNotStacked, EQ TST r1, #8 BNE underflow_error B huge_error ldfp_overflow LDR r0, [r0, #0] CMPS r0, #0 BPL huge_error huge_negative_result MOV r0, #ERANGE LDFD f0, negative_huge_val ; @@@@!!!! Set_errno LoadStaticAddress |__errno|, r1 STR r0, [r1, #0] Return ,LinkNotStacked negative_huge_val DCD &FFEFFFFF ; put constant where it is easy to find DCD &FFFFFFFF huge_error MOV r0, #ERANGE LDFD f0, huge_val ; @@@@!!!! B Set_errno huge_val DCD &7FEFFFFF ; put constant where it is easy to find DCD &FFFFFFFF huge_errorf MOV r0, #ERANGE LDFS f0, huge_valf B Set_errno huge_valf DCD &7F800000 negative_error MOV r0, #EDOM LDFD f0, negative_huge_val ; @@@@!!!! B Set_errno negative_errorf MOV r0, #EDOM LDFS f0, negative_huge_valf ; @@@@!!!! B Set_errno negative_huge_valf DCD &FF800000 ; put constant where it is easy to find underflow_error MOV r0, #ERANGE ; MVFD f0, #0 B Set_errno log10 [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} LDFD f0, [sp], #8 ] CMFE f0, #0 BEQ huge_negative_result BMI negative_error LOGD f0, f0 Return ,LinkNotStacked log [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} LDFD f0, [sp], #8 ] CMFE f0, #0 BEQ huge_negative_result BMI negative_error LGND f0, f0 Return ,LinkNotStacked sqrt [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} LDFD f0, [sp], #8 ] CMFE f0, #0 BMI negative_error SQTD f0, f0 Return ,LinkNotStacked [ {FALSE} sqrtf [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} LDFD f0, [sp], #8 ] MVFS f0, f0 CMFE f0, #0 BMI negative_errorf SQTS f0, f0 Return ,LinkNotStacked ] tan [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} DisableFPInterrupts LDFD f0, [sp], #8 | DisableFPInterrupts ] TAND f0, f0 ReEnableFPInterrupts TST r1, #&07 Return ,LinkNotStacked, EQ B huge_error [ {FALSE} tanf [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} DisableFPInterrupts LDFD f0, [sp], #8 | DisableFPInterrupts ] MVFS f0, f0 TANS f0, f0 ReEnableFPInterrupts TST r1, #&07 Return ,LinkNotStacked, EQ B huge_errorf ] atan [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} LDFD f0, [sp], #8 ] ATND f0, f0 Return ,LinkNotStacked asin [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} DisableFPInterrupts LDFD f0, [sp], #8 | DisableFPInterrupts ] ASND f0, f0 ReEnableFPInterrupts ; A range error is not possible; any error must be a domain error. ; (And the only plausible error flag is IVO, but I don't check). ; Dunno what result is sensible. TST r1, #&07 Return ,LinkNotStacked, EQ B negative_error acos [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} DisableFPInterrupts LDFD f0, [sp], #8 | DisableFPInterrupts ] ACSD f0, f0 ReEnableFPInterrupts ; A range error is not possible; any error must be a domain error. ; (And the only plausible error flag is IVO, but I don't check). ; Dunno what result is sensible. TST r1, #&07 Return ,LinkNotStacked, EQ B negative_error pow [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1, r2, r3} DisableFPInterrupts LDFD f0, [sp], #8 LDFD f1, [sp], #8 | DisableFPInterrupts ] CMFE f0, #0 BEQ POWFirstArgZero POWD f0, f0, f1 ReEnableFPInterrupts ; Plausibly, there may have been either an overflow or IVO error. ; I assume that the former is always a range error, and the latter ; corresponds to one of the possible C domain errors (first arg ; negative, second non-integer). ; (DVZ assumed impossible). TST r1, #&0F Return ,LinkNotStacked, EQ TST r1, #1 BNE negative_error TST r1, #8 BNE underflow_error B huge_error POWFirstArgZero CMFE f1, #0 MVFEQD f0, #1 ; return 1.0 if both args 0.0 ReEnableFPInterrupts Return ,LinkNotStacked, GE B negative_error |_count| ; used when profile option is enabled RemovePSRFromReg lr, ip LDR ip, [lr, #0] ADD ip, ip, #1 STR ip, [lr, #0] ADD pc, lr, #4 ; condition codes are preserved because ; nothing in this code changes them! ; What follows is the newer version of support for the profile option, ; using extra space to record the position in the file that this ; count-point corresponds to. |_count1| ; used when profile option is enabled RemovePSRFromReg lr, ip LDR ip, [lr, #0] ADD ip, ip, #1 STR ip, [lr, #0] ADD pc, lr, #8 ; condition codes are preserved because ; nothing in this code changes them! ; Default stack overflow handler. ErrorBlock StackOverflow, "Stack overflow", C45 |_default_sigstak_handler| ADR r0, E_StackOverflow BL |_kernel_copyerror| B |_kernel_raise_error| END