; Name:    s.swicall
; Purpose: A nicer varargs SWI wrapper.
; Author:  Steve Hunt, Acorn Computers Ltd
;
; extern os_error *swi (int swinum, ...)
; usage: swi (swinum, R0, 33, R1, "foo", END)
; usage: swi (swinum, NONX, R0, 33, R1, "foo", END)
; usage: swi (swinum, R0, 33, R1, "foo", OUT, R0, &var0, R4, &var4, END)
; usage: swi (swinum, R0, 33, R1, "foo", OUT, R0, &var0, CARRY, &carry, END)

                GBLL    ZEROREGS
ZEROREGS        SETL    {TRUE}                  ; If true, unspecified args default to zero.

r0              RN      0
r1              RN      1
r2              RN      2
r3              RN      3
r4              RN      4
r5              RN      5
r6              RN      6
r7              RN      7
r8              RN      8
r9              RN      9
sl              RN      10
fp              RN      11
ip              RN      12
sp              RN      13
lr              RN      14
pc              RN      15

F_END           *       20
F_OUT           *       21
F_CARRY         *       22
F_NONX          *       23

SWIMASK         *       &ff000000
SWIINST         *       &ef000000
SWIXBIT         *       &00020000
RETINST         *       &E51FF004               ; LDR   pc, [pc, #-4]

                AREA    |C$$code|, CODE, READONLY

                EXPORT  swi

swi             MOV     ip, sp                          ; APCS entry
                STMFD   sp!, {r1-r3}                    ; varargs preparation
                STMFD   sp!, {r4-r9, fp, ip, lr, pc}    ; APCS entry
                SUB     fp, ip, #16                     ; APCS entry

                ADD     lr, fp, #4              ; point to first varargs argument

                BIC     r4, r0, #SWIMASK        ; build SWI instruction ...
                ORR     r4, r4, #SWIINST        ; ... assuming XBit version for now
                ORR     r4, r4, #SWIXBIT

        [ ZEROREGS
                MOV     r0, #0                  ; extend stack with zeros
                MOV     r1, #10                 ; room for 10 registers
clear           STR     r0, [sp, #-4]!
                SUBS    r1, r1, #1
                BNE     clear
        |
                SUB     sp, sp, #40             ; registers contain junk by default
        ]

collect         LDR     r0, [lr], #4            ; get next arg

                TEQ     r0, #F_NONX             ; is it "no X bit"?
                BICEQ   r4, r4, #SWIXBIT        ; yes, clear X bit
                BEQ     collect

                TEQ     r0, #F_END              ; if it's END
                SUBEQ   lr, lr, #4              ; then back up so we see it again...
                TEQNE   r0, #F_OUT              ; stop collecting if it's END or OUT,

; XXX assume r0 now holds valid register number - not checked for validity!

                LDRNE   r1, [lr], #4            ; ... get the argument...
                STRNE   r1, [sp, r0, LSL #2]    ; ... and put it in the array
                
                BNE     collect                 ; continue

; Build the SWI on the stack.  Return with an LDR from the stack, as we don't
; have any safe registers (can't rely on lr because we might already be in
; supervisor mode).

                MOV     r0, sp                  ; save address of register table
                LDR     r5, =RETINST            ; LDR pc, [pc, #-4]
                ADD     r6, pc, #return-.-8     ; generate return address
                STMFD   sp!, {r4-r6, lr}        ; Relies on ordering imposed by STM
                LDMIA   r0, {r0-r9}             ; get the registers
                MOV     pc, sp                  ; branch to the bottom of the stack

; If all goes well, we regain control after the SWI right here:-

return          ADD     sp, sp, #12             ; throw away the two instructions and addr
                LDR     lr, [sp], #4            ; regain address of argument list
                STMIA   sp, {r0-r9}             ; save the results back

                MOVVC   r0, #0                  ; if no error, return zero (otherwise return r0)
                MOVCC   r1, #0                  ; set r1 to 1 or 0 as carry flag
                MOVCS   r1, #1
                
scatter         LDR     r2, [lr], #4            ; get the next argument
                TEQ     r2, #F_END              ; is it END?
                BEQ     finished
                TEQ     r2, #F_CARRY            ; is it carry?
                MOVEQ   r2, r1                  ; yes, use the carry flag
                LDRNE   r2, [sp, r2, LSL #2]    ; no, so get [r2] register's value back
                LDR     r3, [lr], #4            ; get address of user's variable
                STR     r2, [r3]                ; store the value there
                B       scatter

finished        LDMEA   fp, {r4-r9, fp, sp, pc}^        ; APCS exit

                END
