; -----------------------------------------------------------------------
|
; sysv.S - Copyright (c) 1998 Red Hat, Inc.
|
;
|
; ARM Foreign Function Interface
|
;
|
; Permission is hereby granted, free of charge, to any person obtaining
|
; a copy of this software and associated documentation files (the
|
; ``Software''), to deal in the Software without restriction, including
|
; without limitation the rights to use, copy, modify, merge, publish,
|
; distribute, sublicense, and/or sell copies of the Software, and to
|
; permit persons to whom the Software is furnished to do so, subject to
|
; the following conditions:
|
;
|
; The above copyright notice and this permission notice shall be included
|
; in all copies or substantial portions of the Software.
|
;
|
; THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
; OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
; IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
; OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
; OTHER DEALINGS IN THE SOFTWARE.
|
; ----------------------------------------------------------------------- */
|
|
;#define LIBFFI_ASM
|
;#include <fficonfig.h>
|
;#include <ffi.h>
|
;#ifdef HAVE_MACHINE_ASM_H
|
;#include <machine/asm.h>
|
;#else
|
;#ifdef __USER_LABEL_PREFIX__
|
;#define CONCAT1(a, b) CONCAT2(a, b)
|
;#define CONCAT2(a, b) a ## b
|
|
;/* Use the right prefix for global labels. */
|
;#define CNAME(x) CONCAT1 (__USER_LABEL_PREFIX__, x)
|
;#else
|
;#define CNAME(x) x
|
;#endif
|
;#define ENTRY(x) .globl CNAME(x); .type CNAME(x),%function; CNAME(x):
|
;#endif
|
|
|
FFI_TYPE_VOID EQU 0
|
FFI_TYPE_INT EQU 1
|
FFI_TYPE_FLOAT EQU 2
|
FFI_TYPE_DOUBLE EQU 3
|
;FFI_TYPE_LONGDOUBLE EQU 4
|
FFI_TYPE_UINT8 EQU 5
|
FFI_TYPE_SINT8 EQU 6
|
FFI_TYPE_UINT16 EQU 7
|
FFI_TYPE_SINT16 EQU 8
|
FFI_TYPE_UINT32 EQU 9
|
FFI_TYPE_SINT32 EQU 10
|
FFI_TYPE_UINT64 EQU 11
|
FFI_TYPE_SINT64 EQU 12
|
FFI_TYPE_STRUCT EQU 13
|
FFI_TYPE_POINTER EQU 14
|
|
; WinCE always uses software floating point (I think)
|
__SOFTFP__ EQU {TRUE}
|
|
|
AREA |.text|, CODE, ARM ; .text
|
|
|
; a1: ffi_prep_args
|
; a2: &ecif
|
; a3: cif->bytes
|
; a4: fig->flags
|
; sp+0: ecif.rvalue
|
; sp+4: fn
|
|
; This assumes we are using gas.
|
;ENTRY(ffi_call_SYSV)
|
|
EXPORT |ffi_call_SYSV|
|
|
|ffi_call_SYSV| PROC
|
|
; Save registers
|
stmfd sp!, {a1-a4, fp, lr}
|
mov fp, sp
|
|
; Make room for all of the new args.
|
sub sp, fp, a3
|
|
; Place all of the ffi_prep_args in position
|
mov ip, a1
|
mov a1, sp
|
; a2 already set
|
|
; And call
|
mov lr, pc
|
mov pc, ip
|
|
; move first 4 parameters in registers
|
ldr a1, [sp, #0]
|
ldr a2, [sp, #4]
|
ldr a3, [sp, #8]
|
ldr a4, [sp, #12]
|
|
; and adjust stack
|
ldr ip, [fp, #8]
|
cmp ip, #16
|
movge ip, #16
|
add sp, sp, ip
|
|
; call function
|
mov lr, pc
|
ldr pc, [fp, #28]
|
|
; Remove the space we pushed for the args
|
mov sp, fp
|
|
; Load a3 with the pointer to storage for the return value
|
ldr a3, [sp, #24]
|
|
; Load a4 with the return type code
|
ldr a4, [sp, #12]
|
|
; If the return value pointer is NULL, assume no return value.
|
cmp a3, #0
|
beq call_epilogue
|
|
; return INT
|
cmp a4, #FFI_TYPE_INT
|
streq a1, [a3]
|
beq call_epilogue
|
|
; return FLOAT
|
cmp a4, #FFI_TYPE_FLOAT
|
[ __SOFTFP__ ;ifdef __SOFTFP__
|
streq a1, [a3]
|
| ;else
|
stfeqs f0, [a3]
|
] ;endif
|
beq call_epilogue
|
|
; return DOUBLE or LONGDOUBLE
|
cmp a4, #FFI_TYPE_DOUBLE
|
[ __SOFTFP__ ;ifdef __SOFTFP__
|
stmeqia a3, {a1, a2}
|
| ;else
|
stfeqd f0, [a3]
|
] ;endif
|
beq call_epilogue
|
|
; return SINT64 or UINT64
|
cmp a4, #FFI_TYPE_SINT64
|
stmeqia a3, {a1, a2}
|
|
call_epilogue
|
ldmfd sp!, {a1-a4, fp, pc}
|
|
;.ffi_call_SYSV_end:
|
;.size CNAME(ffi_call_SYSV),.ffi_call_SYSV_end-CNAME(ffi_call_SYSV)
|
ENDP
|
|
|
RESERVE_RETURN EQU 16
|
|
; This function is called by the trampoline
|
; It is NOT callable from C
|
; ip = pointer to struct ffi_closure
|
|
IMPORT |ffi_closure_SYSV_inner|
|
|
EXPORT |ffi_closure_SYSV|
|
|ffi_closure_SYSV| PROC
|
|
; Store the argument registers on the stack
|
stmfd sp!, {a1-a4}
|
; Push the return address onto the stack
|
stmfd sp!, {lr}
|
|
mov a1, ip ; first arg = address of ffi_closure
|
add a2, sp, #4 ; second arg = sp+4 (points to saved a1)
|
|
; Allocate space for a non-struct return value
|
sub sp, sp, #RESERVE_RETURN
|
mov a3, sp ; third arg = return value address
|
|
; static unsigned int
|
; ffi_closure_SYSV_inner (ffi_closure *closure, char *in_args, void *rvalue)
|
bl ffi_closure_SYSV_inner
|
; a1 now contains the return type code
|
|
; At this point the return value is on the stack
|
; Transfer it to the correct registers if necessary
|
|
; return INT
|
cmp a1, #FFI_TYPE_INT
|
ldreq a1, [sp]
|
beq closure_epilogue
|
|
; return FLOAT
|
cmp a1, #FFI_TYPE_FLOAT
|
[ __SOFTFP__ ;ifdef __SOFTFP__
|
ldreq a1, [sp]
|
| ;else
|
stfeqs f0, [sp]
|
] ;endif
|
beq closure_epilogue
|
|
; return DOUBLE or LONGDOUBLE
|
cmp a1, #FFI_TYPE_DOUBLE
|
[ __SOFTFP__ ;ifdef __SOFTFP__
|
ldmeqia sp, {a1, a2}
|
| ;else
|
stfeqd f0, [sp]
|
] ;endif
|
beq closure_epilogue
|
|
; return SINT64 or UINT64
|
cmp a1, #FFI_TYPE_SINT64
|
ldmeqia sp, {a1, a2}
|
|
closure_epilogue
|
add sp, sp, #RESERVE_RETURN ; remove return value buffer
|
ldmfd sp!, {ip} ; ip = pop return address
|
add sp, sp, #16 ; remove saved argument registers {a1-a4} from the stack
|
mov pc, ip ; return
|
|
ENDP ; ffi_closure_SYSV
|
|
END
|