; 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 rlib.s.asmdefs 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 |_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$udivide| EXPORT |x$uremainder| EXPORT |x$divide| EXPORT |x$divtest| EXPORT |x$remainder| 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 |_ldfp| EXPORT |_stfp| EXPORT |__rt_stkovf_split_small| EXPORT |__rt_stkovf_split_big| EXPORT |__rt_udiv| EXPORT |__rt_sdiv| EXPORT |__rt_divtest| EXPORT |__rt_rd1chk| EXPORT |__rt_rd2chk| EXPORT |__rt_rd4chk| EXPORT |__rt_wr1chk| EXPORT |__rt_wr2chk| EXPORT |__rt_wr4chk| 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 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! |x$multiply| ; a1 := a2 * a1. ; sets a2 to 0 and a3 to a copy of the product. MOV a3, #0 m0loop MOVS a2, a2, LSR #1 ADDCS a3, a3, a1 ADD a1, a1, a1 BNE m0loop MOV a1, a3 Return ,LinkNotStacked |x$remainder| B |_kernel_srem| IMPORT |_kernel_copyerror| IMPORT |_kernel_fault| |__rt_divtest| |x$divtest| ; test for division by zero (used when division is voided) CMPS a1, #0 Return ,LinkNotStacked, NE STMFD sp!, {r0} ADR r0, E_DivideByZero B |_kernel_fault| |__rt_sdiv| |x$divide| B |_kernel_sdiv| |__rt_udiv| |x$udivide| B |_kernel_udiv| |x$uremainder| B |_kernel_urem| |__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" wimp_title DCB "WindowManager", 0 utility_title DCB "UtilityModule", 0 [ :DEF:DEFAULT_TEXT postmortem_title DCB "Postmortem", 0 ] postmortem_tag DCB "C65", 0 ALIGN n_module_lookup EQU 18 wimp_preinitflag EQU &80000000 zp_wimpdomain EQU &ff8 IMPORT |_kernel_unwind| |_postmortem| ; Prepare window manager for output ; First check this is a wimp task STMFD sp!, {r0-r5, r14} MOV r0, #n_module_lookup ADR r1, utility_title SWI XOS_Module BVS postmortem2 LDR r0, [r3, #5 * 4] MOV r1, #0 ADD r0, r0, r3 postmortem3 LDRB r2, [r0], #1 CMP r2, #9 ORREQ r1, r1, #7 ADD r1, r1, #1 CMP r1, #16 BCC postmortem3 LDRB r1, [r0] SUB r1, r1, #'0' LDRB r2, [r0, #2] SUB r2, r2, #'0' ADD r1, r1, r1, LSL #2 ADD r1, r2, r1, LSL #1 LDRB r2, [r0, #3] SUB r2, r2, #'0' ADD r1, r1, r1, LSL #2 ADD r1, r2, r1, LSL #1 CMP r1, #202 BCC postmortem2 MOV r0, #0 SWI XTaskWindow_TaskInfo MOVVS r0, #0 CMP r0, #0 BNE postmortem0 MOV r0, #3 SWI XWimp_ReadSysInfo BVS postmortem0 CMP r0, #0 BEQ postmortem0 B postmortem4 postmortem2 MOV r0, #0 SWI XWimp_ReadSysInfo BVS postmortem0 CMP r0, #0 BEQ postmortem0 ; In desktop, now check Wimp_Initialise has been called MOV r0, #n_module_lookup ADR r1, wimp_title SWI XOS_Module BVS postmortem0 MOV r0, #0 LDR r0, [r0, #zp_wimpdomain] LDR r0, [r4, r0] TST r0, #wimp_preinitflag BNE postmortem0 ; If so reopen command window postmortem4 [ :DEF:DEFAULT_TEXT ADR r0, postmortem_title ADR r1, postmortem_tag | ADR r0, postmortem_tag ] BL |_kernel_getmessage| SWI XWimp_CommandWindow postmortem0 LDMFD sp!, {r0-r5} LDR a3, mesg CMP a2, a3 BLEQ |_sys_msg| ; BL<cond> 32-bit OK postmortem1 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| |__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 sin [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} LDFD f0, [sp], #8 ] SIND f0, f0 Return ,LinkNotStacked cos [ :LNOT: FloatingPointArgsInRegs STMFD sp!, {r0, r1} LDFD f0, [sp], #8 ] COSD 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 negative_error MOV r0, #EDOM LDFD f0, negative_huge_val ; @@@@!!!! B Set_errno 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 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 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