; 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.
;

                AREA    |C$$code|, CODE, READONLY

        EXPORT  |_swix|
        EXPORT  |_swi|
|_swi|

; Construct a stack frame that looks something like this:
;       LDMIA   r12!, {r0..rn}      ; Or NOP if no input regs
;       ADD     Rb, R12, #Nout * 4  ; Or NOP if no parameter block
;       SWI     xxxxxx
;       MOV     R0, Rn              ; Use ADD because Rn is correct bitfield
;       B       SWIReturn
;       saved r4-r11,lr
;       saved r1
;       saved input values (r2...rn)

        STMFD   sp!, {r2-r3}            ; Save r1 and put 1st two variadic args on stack
        STMDB   sp!, {r1, r4-r9, lr}
        ADR     r6, SWIReturn-4
        B       swix0
|_swix|
        ORR     r0, r0, #&20000
        STMDB   sp!, {r2-r3}
        STMDB   sp!, {r1, r4-r9, lr}
        ADR     r6, SWIXReturn-4
swix0
        ORR     r3, r0, #&ef000000      ; Construct SWI instruction
        MOV     r0, r1, LSL #22         ; Construct LDMIA R12!, {regs} instruction
        MOVS    r0, r0, LSR #22         ; {regs} = {} (IE no input regs) we must not
        ORRNE   r0, r0, #&e8000000      ; use an LDMIA R12!, {} instruction as this is an
        ORRNE   r0, r0, #&00bc0000      ; invalid instruction, we use a suitable NOP instead.
        MOV     r5, r1, LSR #16
        AND     r5, r5, #&f
        ORR     r5, r5, #&e1000000
        ORR     r5, r5, #&a00000
        ANDS    r2, r1, #&800
        BLNE    BuildBlockInst
        SUB     r6, r6, sp
        MOV     r6, r6, LSR #2
        BIC     r6, r6, #&ff000000
        ADD     r6, r6, #&ea000000
        STMDB   sp!, {r0,r2,r3,r5,r6}
        ADD     r12, sp, #(5+8)*4       ; Point R12 at input regs on stack.
  [ StrongARM
    ;oh my gawd ...
    ;in case we are running on StrongARM, have to synchronise with respect to modified code
    ;this is all highly inefficient, and the mechanism needs sorting properly later
    STMDB  sp!,{r0-r2,lr}           ;don't know whether any of these are trashable
    MOV    r0,#1                    ;means range specified in r1,r2
    ADD    r1,sp,#4*4               ;start address (allowing for stacked r0-r2,lr)
    ADD    r2,r1,#4*4               ;end address (inclusive) for 5 words of code
    SWI    XOS_SynchroniseCodeAreas ;do the necessary
    LDMIA  sp!,{r0-r2,lr}
  ]
        MOV     pc, sp                  ; Call routine on stack
SWIReturn
        STR     pc, [sp, #4*4]!         ; no PC+12/PC+8 problem (old/new ARM) since only condition codes used
        LDR     lr, [sp, #1*4]
        MOVS    lr, lr, ASL #1          ; Shift out setting C if R0 to be written, N
        LDRCS   lr, [r12], #4           ; if R1 to be written.
        STRCS   r0, [lr]
        LDRMI   lr, [r12], #4
        STRMI   r1, [lr]
        LDR     lr, [sp, #1*4]
        B       ReturnTail
SWIXReturn
        STR     pc, [sp, #4*4]!         ; no PC+12/PC+8 problem (old/new ARM) since only condition codes used
        LDR     lr, [sp, #1*4]
        BVS     VSetReturn
        MOVS    lr, lr, ASL #1          ; Shift out setting C if R0 to be written, N
        LDRCS   lr, [r12], #4           ; if R1 to be written.
        STRCS   r0, [lr]
        LDRMI   lr, [r12], #4
        STRMI   r1, [lr]
        LDR     lr, [sp, #1*4]
        TST     lr, #&f0000
        MOVEQ   r0, #0
ReturnTail
        MOVS    lr, lr, ASL #3          ; Shift 2 bits each time for the next 2 regs
        LDRCS   r1, [r12], #4
        STRCS   r2, [r1]
        LDRMI   r1, [r12], #4
        STRMI   r3, [r1]
        AND     lr, lr, #&ff000000
        MOVS    lr, lr, ASL #2
        LDRCS   r1, [r12], #4
        STRCS   r4, [r1]
        BEQ     VSetReturn              ; Typically saves 16S - (3S + 1N)
        LDRMI   r1, [r12], #4
        STRMI   r5, [r1]
        MOVS    lr, lr, ASL #2
        LDRCS   r1, [r12], #4
        STRCS   r6, [r1]
        LDRMI   r1, [r12], #4
        STRMI   r7, [r1]
        MOVS    lr, lr, ASL #2
        LDRCS   r1, [r12], #4
        STRCS   r8, [r1]
        LDRMI   r1, [r12], #4
        STRMI   r9, [r1]
        MOVS    lr, lr, ASL #2
        LDRCS   r9, [sp]
        LDRCS   r1, [r12], #4
        STRCS   r9, [r1]
VSetReturn
        ADD     sp, sp, #2*4
        LDMIA   sp!, {r4-r9,lr}
        ADD     sp, sp, #2 * 4
        MOVS    pc, lr

BuildBlockInst
        MOV     r4, #6
        AND     r2, r1, #&f000
        ORR     r2, r2, #&e2000000
        ORR     r2, r2, #&008c0000
BuildBlockInst1
        MOVS    r1, r1, ASL #2
        ADDCS   r2, r2, #4
        ADDMI   r2, r2, #4
        SUBS    r4, r4, #1
        BNE     BuildBlockInst1
        MOVS    pc, lr

        END