; 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 -*-
;* module initialisation for shared libraries
;* Lastedit: 03 Apr 90 10:51:05 by Harry Meekings *
; Copyright (C) Acorn Computers Ltd., 1988
;
; IDJ: 19-Jul-91: removed message C03

        GBLL    SharedLibrary
SharedLibrary   SETL    {TRUE}

        GBLL    NoStubEntries
NoStubEntries   SETL    {TRUE}  ; stop h_modmacro defining Entry

        AREA    |AA$$code|, CODE, READONLY

        GET     s.h_Regs
        GET     s.h_Brazil
        GET     s.h_stack
        GET     s.h_modmacro
        GET     s.h_workspc

        GET     Hdr:Proc

        IMPORT  |Lib$$Init$$Base|
        IMPORT  |Lib$$Init$$Limit|
        IMPORT  |Image$$ZI$$Base|
 [ Code_Destination = "RAM"
        IMPORT  |__RelocCode|
 ]
        IMPORT  |_kernel_copyerror|
        IMPORT  |_kernel_getmessage|

        EXPORT  |_Lib$Reloc$Off|
        EXPORT  |_Mod$Reloc$Off|
        EXPORT  |_Shared_Lib_Module_SWI_Code|
        EXPORT  |_Shared_Lib_Module_Init_Code|
        EXPORT  |_Shared_Lib_Module_Die_Code|

|_Lib$Reloc$Off|        *       -SL_Lib_Offset
|_Mod$Reloc$Off|        *       -SL_Client_Offset

OverflowBit             *       &10000000

n_module_claim EQU 6

sharedclibrary_path
        DCB     "SharedCLibrary$Path"
        DCB     0
risc_oslibrary_path
        DCB     "RISC_OSLibrary$Path"
        DCB     0

sharedclibrary_resources
        DCB     "Resources:$.Resources.CLib."
        DCB     0
risc_oslibrary_resources
        DCB     "Resources:$.Resources.RISC_OSLib."
        DCB     0

        ALIGN

|_Shared_Lib_Module_Init_Code|
        STMFD   r13!, {r7-r11, lr}
 [ Code_Destination = "RAM"
        BL      |__RelocCode|
 ]
        MOV     r14, #0
        STR     r14, [r14, #CLibWorkSpace]
        STR     r14, [r14, #RISCOSLibWorkSpace]

        ADR     r0, sharedclibrary_path
        ADR     r1, sharedclibrary_resources
        MOV     r5, #?sharedclibrary_resources
        BL      setresourcevar

        ADR     r0, risc_oslibrary_path
        ADR     r1, risc_oslibrary_resources
        MOV     r5, #?risc_oslibrary_resources
        BL      setresourcevar

        CLRV
        LDMFD   r13!, {r7-r11, pc}

setresourcevar
        MOV     r11, lr
        MOV     r10, r0
        MOV     r2, #-1
        MOV     r3, #0
        MOV     r4, #0
        SWI     ReadVarVal
        MOV     r0, r10
        CMP     r2, #0
        MOVEQ   r2, r5
        MOVEQ   r3, #0
        MOVEQ   r4, #0
        SWIEQ   SetVarVal
        MOV     pc, r11

XMessageTrans_CloseFile EQU &61504 ; Put in hdr file someday

|_Shared_Lib_Module_Die_Code|
        STMDB   r13!, {lr}

        MOV     r3, #0
        LDR     r2, [r3, #CLibWorkSpace]
        CMP     r2, #0
        BEQ     %F00
        MOV     r0, r2
        SWI     XMessageTrans_CloseFile
        MOV     r0, #Module_Free
        SWI     Module
        STR     r3, [r3, #CLibWorkSpace]
00
        CLRV
        LDMIA   r13!, {pc}

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; This is the SWI Lib_Init code
;
; Entered with r0 pointer to description of stubs present in image
;                 each having format
;                   library chunk id
;                   entry vector base
;                   entry vector limit
;                   static data base
;                   static data limit
;                 terminated by an entry with stub id -1
;                 For each of these, there must be an entry in the shared
;                 library description (in the Lib$$Init area) with the same
;                 id and the the same static data size.
;              r1 pointer to low end of workspace
;              r2 pointer to high end of workspace
;              r3 base of client's zero-initialise area (at end of statics)
;              r4 pointer to start of client's statics
;              r5 pointer to end of clients's statics
;              (old stubs)  r6 = 0
;              r6 [0] = 0 if client is running in a 26-bit mode (may still be APCS-32)
;                     = 1 if client is running in a 32-bit mode (=> must be APCS-32,
;                       and address constant table must follow vectors)
;              r6 [1:15] = 0
;              r6 [16:31] = requested root stack size (Kb)
; Returns with stub vector patched
;              if input r5>r4, user statics copied and [sl, #SL_Client_Offset]
;               initialised
;              library statics copied and [sl, #SL_Lib_Offset] initialised
;              r1 stack base
;              r2 stack top (sp value)
;              r6 = library version number
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        ^ 0
        ; Format of Lib$$Init area entry
LI_Id           #       4
LI_EntryBase    #       4
LI_EntryEnd     #       4
LI_DataStart    #       4
LI_DataEnd      #       4
LI_ItemSize     #       0


|_Shared_Lib_Module_SWI_Code|
        ; Relative SWI number in r11.
        ;  0  old-style init.  Client uses APCS_A.  No longer supported.
        ;  1  new-style init.  Client uses APCS_U; so must this module.
        ;  2  (new-style) module init.  Stack is SWI stack, not in the heap.
        ;  3  APCS-32 init.  Client uses APCS-3/32bit.
        ;  4  APCS-32 module init.  Stack is SWI stack, not in the heap.

        EntryS  "r0-r6"

        ; Check SWI in valid range
        CMP     r11, #1
        ADRLO   r0, E_OldAPCS_A
        BLO     Failed
      [ {CONFIG}<>26
        CMP     r11, #3
        ADRLO   r0, E_OldAPCS_R
        BLO     Failed
      ]

        CMP     r11, #4
        ADRHI   r0, E_UnknownSWI
        BHI     Failed

        CLRPSR  F_bit:OR:I_bit, lr      ; ensure IRQs enabled
        NOP

        ; Check memory constraints work:
        ; lr = end of required workspace (data + stack), ie stack top for non-modules
        ; r12 = stack base
        SUBS    lr, r5, r4              ; size of client's statics
        MOVMI   lr, #0                  ; no client statics really.
        CMP     r11, #2                 ; module?
        CMPNE   r11, #4
        MOVEQ   r12, sp, LSR #20
        MOVEQ   r12, r12, ASL #20
        ADDEQ   lr, lr, r1
        BEQ     CheckEnoughStore

        ADD     r12, r1, lr             ; heap base plus size of copied statics
        ; For things linked with old stubs r6 may be zero - if so,
        ; we use the old default root stack size
        MOVS    r6, r6, LSR #6
        MOVEQ   r6, #OldRootStackSize
        ADD     lr, r12, r6             ; plus size of root stack
CheckEnoughStore
        CMP     lr, r2                  ; must fit within available workspace
        ADRGT   r0, E_BadMemory
        BGT     Failed

      [ Proc_RegOffset = 0
        STMIA   r13, {r2, r12, lr}      ; return values of r0, r1 & r2 (Data end, stack base and stack top)
      |
        [ Proc_RegOffset = 4
        STMIB   r13, {r2, r12, lr}      ; return values of r0, r1 & r2 (Data end, stack base and stack top)
        |
        ! 1, "Proc_RegOffset = ?"
        ]
      ]
        CMP     r11, #2                 ; module?
        CMPNE   r11, #4
        STRNE   r6, [r12, #SC_size]     ; Start filling stack chunk header for non-modules

        ; Copy the non-zeroed client statics
        SUBS    lr, r5, r4              ; see again whether client statics to copy
        MOVMI   r4, r1
        SUBS    r6, r3, r4
        BLE     ZeroInitClientStatics
CopyClientStatics
        LDR     lr, [r4], #+4
        STR     lr, [r1], #+4
        SUBS    r6, r6, #4
        BNE     CopyClientStatics

        ; Zero the client statics which need zeroing
ZeroInitClientStatics
        SUB     lr, r1, r4              ; the value for SL_Client_Offset
        SUBS    r5, r5, r3
        BLE     DoneClientStatics
        MOV     r6, #0
01      STMIA   r1!, {r6}
        SUBS    r5, r5, #4
        BGT     %B01

        ; Client statics copied/zeroed, fill in SC_Client_Offset for -zm1 clients
DoneClientStatics
        STR     lr, [r12, #SC_SLOffset+SL_Client_Offset]

        ; r5 is used to hold SL_Lib_Offset - if a library chunk has an different value
        ; its time to barf! r5=0 is the initial value to indicate 'r5 isn't set yet'
        MOV     r5, #0

NextLibraryChunk
        ; Pick up:
        ; r14 = Start of our table of libraries
        ; r12 = End of our table of libraries
        ; r1 = next client library chunk number
        ; r2 = next client library chunk branch table start
        ; r3 = next client library chunk branch table end
        LDR     r14, =|Lib$$Init$$Base|
        LDR     r12, =|Lib$$Init$$Limit|
        LDMIA   r0!, {r1, r2, r3}

        ; If end of client's library table we've finished
        CMP     r1, #0
        BLT     EndStubInit

FindLibraryChunk
        ; If finished with our table of libraries then the client's chunk isn't recognised
        CMP     r14, r12
        ADRGE   r0, E_UnknownLib
        BGE     Failed

        ; Check if this chunk matches the client's, if yes, then drop through
        LDR     r4, [r14], #LI_ItemSize
        BIC     r4, r4, #library_segment_is_ROM_only
        CMP     r4, r1
        BNE     FindLibraryChunk

FoundLibraryChunk
        ; We've now matched a client library chunk to a library chunk we know about
        ; so, we should now patch the branches and copy and initialise the data

        ; Check not attempting to bind ROM chunk to RAM application
        LDR     r4, [r14, #LI_Id - LI_ItemSize]
        TST     r4, #library_segment_is_ROM_only
        TEQNE   r2, r3
        ADRNE   r0, E_UnknownLib
        BNE     Failed

        ; I suppose it would be friendly to check here that there are
        ; no fewer entries in the library than in the stub.
        ; So let's do it:
        LDR     r12, StubInitValue
        LDR     r1, [r14, #LI_EntryBase-LI_ItemSize]
        LDR     r4, [r14, #LI_EntryEnd-LI_ItemSize]
        SUB     r4, r4, r1              ; r4 = size of our table
        ADD     r4, r2, r4              ; r4 = end of client table we know
01      CMP     r4, r3
        STRLO   r12, [r3, #-4]!         ; fill in excess with MOV PC,#0
        BLO     %BT01

        ; Branch patchup for SWI Lib_Init1 to 4
        ; r2 = entry vector base
        ; r3 = entry vector limit
        ; r6 bit 0 set => 32-bit mode (so BL not valid), and address constant table
        ; of same size follows immediately after

        ; UNFORTUNATELY, r6 bit 0 is not set by early versions of the 32-bit
        ; module stubs. So we can't rely on it. Instead, if we're in {CONFIG}=32
        ; we know we only support the APCS-32 Init SWIs, so we know they must
        ; have supplied sufficient space for the table, and we were built for
        ; a 32-bit OS. We will thus always use B if {CONFIG}=26 and LDR PC if
        ; {CONFIG}=32.

 [ {CONFIG} <> 26
        ; Create a table of LDR PC,&xxx followed by the address constants

        LDR     r1, =&E59FF000          ; LDR PC,[PC,#+0]
        SUB     r4, r3, r2              ; r4 = size of table (remember for later)
        SUBS    r6, r4, #8              ; r6 = offset to use in LDR
        BICMI   r1, r1, #1:SHL:23       ; handle the negative case (just possible
        RSBMI   r6, r6, #0              ; for a single entry stub table)
        CMP     r6, #&1000              ; Offset must be <= &FFF
        ADRHS   r0, E_UnknownLib        ; Can't be bothered to make a new error
        BHS     Failed

        ORR     r6, r1, r6
FixLDRs
        CMP     r2, r3
        STRNE   r6, [r2], #4
        BNE     FixLDRs                 ; Write the LDR PC,[PC,#<xxx>] into each entry

        ADD     r3, r2, r4              ; r2, r3 now point to address constant table

        LDR     r4, [r14, #LI_EntryBase-LI_ItemSize]    ; Our entry table
FixAddresses
        CMP     r2, r3
        BEQ     ChunkEntriesDone
        LDR     r1, [r4], #+4
        MOV     r1, r1, ASL #8         ; sign-extend branch offset
        ADD     r1, r4, r1, ASR #6     ; and convert to bytes
        ADD     r1, r1, #8-4           ; +8 for pc, -4 for ,#+4 above
        LDR     r6, [r2], #4           ; patch the stub entry only if it's
        CMP     r6, r1                 ; not already right.
        STRNE   r1, [r2, #-4]
        B       FixAddresses
 |
        ; Create a table of BL &xxx

FixBranches
        LDR     r4, [r14, #LI_EntryBase-LI_ItemSize]    ; Our entry table
FixEntries
        CMP     r2, r3
        BEQ     ChunkEntriesDone
        LDR     r1, [r4]
        ADD     r1, r1, r4, LSR #2
        SUB     r1, r1, r2, LSR #2
        BIC     r1, r1, #&FF000000
        ORR     r1, r1, #&EA000000     ; le branch!
        ADD     r4, r4, #4
        LDR     r6, [r2], #4           ; patch the stub entry only if it's
        CMP     r6, r1                 ; not already right.
        STRNE   r1, [r2, #-4]
        B       FixEntries
 ]

ChunkEntriesDone
        ; Having patched up the branch table, lets copy the library's static data

        ; The space reserved in the image for static data for this chunk
        ; must match the actual size in the shared library.
        LDMIA   r0!, {r2, r3}
        LDMDB   r14, {r4, r6}
        SUB     r1, r2, r3
        ADD     r1, r1, r6
        SUBS    r1, r1, r4
 [ 1 = 1
        ADRNE   r0, E_StaticSizeWrong
        BNE     Failed
 |
        BEQ     staticsizeok
        STMDB   sp!, {r0, r1, r2}
   [ :DEF:DEFAULT_TEXT
        ADR     r0, stubdatasize_text
        ADR     r1, stubdatasize_tag
   |
        ADR     r0, stubdatasize_tag
   ]
        BL      |_kernel_getmessage|
        BL      writectrl
        SUB     r0, r3, r2
        SUB     r1, sp, #12
        MOV     r2, #9
        SWI     &d4
        SWI     2
        SWI     3
   [ :DEF:DEFAULT_TEXT
        ADR     r0, libdatasize_text
        ADR     r1, libdatasize_tag
   |
        ADR     r0, libdatasize_tag
   ]
        BL      |_kernel_getmessage|
        BL      writectrl
        SUB     r0, r6, r4
        MOV     r1, sp
        MOV     r2, #9
        SWI     &d4
        SWI     2
        SWI     3
        ADD     sp, sp, #8
        LDMIA   sp!, {r0, r1, r2}
        ADR     r0, E_StaticSizeWrong
        B       Failed

writectrl
        STMDB   sp!, {r1,lr}
        MOV     r1, r0
01      LDRB    r0, [r1], #1
        CMP     r0, #" "
        LDMCCIA sp!, {r1,pc}
        SWI     0
        B       %BT01

stubdatasize_tag
        DCB     "C67",0
libdatasize_tag
        DCB     "C68",0

  [ :DEF:DEFAULT_TEXT
stubdatasize_text
        DCB     "Stub data size = ",0
libdatasize_text
        DCB     "Library data size = ",0
        ALIGN
  ]

staticsizeok
 ]
        SUBS    r3, r3, r2              ; size of library chunk statics
        BLE     NextLibraryChunk        ; no statics for this chunk

        ; and the offset must agree with that for all earlier chunks

        LDR     r1, [r13, #Proc_RegOffset + 4]
        LDR     r1, [r1, #SC_SLOffset+SL_Client_Offset]
        ADD     r2, r2, r1              ; relocate address to copy to!!
        SUB     r1, r2, r4
        CMP     r5, #0
        CMPNE   r5, r1
        ADRNE   r0, E_StaticOffsetInconsistent
        BNE     Failed
        MOV     r5, r1

        ; Copy the data from our fixed static data area to the clients dynamic
        ; static data area. No zero initialised data at all.
CopyLibStatics
        LDR     r1, [r4], #+4
        STR     r1, [r2], #+4
        SUBS    r3, r3, #4
        BNE     CopyLibStatics

        LDR     r1, [r14, #LI_Id-LI_ItemSize]
        CMP     r1, #1                  ; was this the library kernel?
        BNE     NextLibraryChunk
        CMP     r11, #3
        ORRGE   r3, r3, #ClientFlag_APCS_32
05      STRB    r3, [r2, #-32*4]        ; yuck! - set ClientFlags
        B       NextLibraryChunk

EndStubInit
        ; Set up SL_Lib_Offset
        LDR     r12, [r13, #Proc_RegOffset + 4]          ; r1 out
        STR     r5, [r12, #SC_SLOffset+SL_Lib_Offset]

  [ StrongARM
    ; patched branch code requires SynchroniseCodeAreas
    MOV    r0,#0                     ;fully synchronise (too lazy to narrow down address range)
    SWI    XOS_SynchroniseCodeAreas
  ]

        MOV     r6, #LibraryVersionNumber
Proc_RegList    SETS "r0-r5, r12"  ; don't restore r6 (hacky :))
        EXITS

Failed
        ; Here with r0=error block if failed for one reason or another
        BL      |_kernel_copyerror|
        STR     r0, [r13, #Proc_RegOffset]
        EXITVS

StubInitValue
        MOV     pc, #0

        ErrorBlock UnknownSWI, "SWI value out of range for module %0", BadSWI
        ErrorBlock BadMemory, "Not enough memory for C library", C01
        ErrorBlock UnknownLib, "Unknown library chunk", C02
        ErrorBlock StaticSizeWrong, "Static data size in library and stub disagree", C04
        ErrorBlock StaticOffsetInconsistent, "Static data offset not the same for all library chunks", C05
        ErrorBlock OldAPCS_A, "Calling standard APCS-A no longer supported by C library", C71, withdefault
 [ {CONFIG}<>26
        ErrorBlock OldAPCS_R, "Application is not 32-bit compatible", C72
 ]
        ALIGN

        LTORG

        END