; 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