/*

* Copyright (c) 2013 Miodrag Vallat.  <miod@openbsd.org>
*
* 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 THE AUTHORS OR COPYRIGHT HOLDERS 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.
*/

/*

* vax Foreign Function Interface
*/

define LIBFFI_ASM

include <fficonfig.h> include <ffi.h>

.text

/*

* void *                                       %r0
* ffi_call_elfbsd(extended_cif *ecif,          4(%ap)
*                 unsigned bytes,              8(%ap)
*                 unsigned flags,              12(%ap)
*                 void *rvalue,                16(%ap)
*                 void (*fn)());               20(%ap)
*/
       .globl  ffi_call_elfbsd
       .type   ffi_call_elfbsd,@function
       .align  2

ffi_call_elfbsd:

.word   0x00c           # save R2 and R3

# Allocate stack space for the args
subl2   8(%ap), %sp

# Call ffi_prep_args
pushl   %sp
pushl   4(%ap)
calls   $2, ffi_prep_args

# Get function pointer
movl    20(%ap), %r1

# Build a CALLS frame
ashl    $-2, 8(%ap), %r0
pushl   %r0             # argument stack usage
movl    %sp, %r0        # future %ap
# saved registers
bbc     $11, 0(%r1), 1f
pushl   %r11

1: bbc $10, 0(%r1), 1f

pushl   %r10

1: bbc $9, 0(%r1), 1f

pushl   %r9

1: bbc $8, 0(%r1), 1f

pushl   %r8

1: bbc $7, 0(%r1), 1f

pushl   %r7

1: bbc $6, 0(%r1), 1f

pushl   %r6

1: bbc $5, 0(%r1), 1f

pushl   %r5

1: bbc $4, 0(%r1), 1f

pushl   %r4

1: bbc $3, 0(%r1), 1f

pushl   %r3

1: bbc $2, 0(%r1), 1f

pushl   %r2

1:

pushal  9f
pushl   %fp
pushl   %ap
movl    16(%ap), %r3    # struct return address, if needed
movl    %r0, %ap
movzwl  4(%fp), %r0     # previous PSW, without the saved registers mask
bisl2   $0x20000000, %r0 # calls frame
movzwl  0(%r1), %r2
bicw2   $0xf003, %r2    # only keep R11-R2
ashl    $16, %r2, %r2
bisl2   %r2, %r0        # saved register mask of the called function
pushl   %r0     
pushl   $0
movl    %sp, %fp

# Invoke the function
pushal  2(%r1)          # skip procedure entry mask
movl    %r3, %r1
bicpsw  $0x000f
rsb

9:

# Copy return value if necessary
tstl    16(%ap)
jeql    9f
movl    16(%ap), %r2

bbc     $0, 12(%ap), 1f # CIF_FLAGS_CHAR
movb    %r0, 0(%r2)
brb     9f

1:

bbc     $1, 12(%ap), 1f # CIF_FLAGS_SHORT
movw    %r0, 0(%r2)
brb     9f

1:

bbc     $2, 12(%ap), 1f # CIF_FLAGS_INT
movl    %r0, 0(%r2)
brb     9f

1:

bbc     $3, 12(%ap), 1f # CIF_FLAGS_DINT
movq    %r0, 0(%r2)
brb     9f

1:

movl    %r1, %r0        # might have been a struct
#brb    9f

9:

ret

/*

* ffi_closure_elfbsd(void);
* invoked with %r0: ffi_closure *closure
*/
       .globl  ffi_closure_elfbsd
       .type   ffi_closure_elfbsd, @function
       .align  2

ffi_closure_elfbsd:

.word   0

# Allocate room on stack for return value
subl2   $8, %sp

# Invoke the closure function
pushal  4(%ap)          # calling stack
pushal  4(%sp)          # return value
pushl   %r0             # closure
calls   $3, ffi_closure_elfbsd_inner

# Copy return value if necessary
bitb    $1, %r0         # CIF_FLAGS_CHAR
beql    1f
movb    0(%sp), %r0
brb     9f

1:

bitb    $2, %r0         # CIF_FLAGS_SHORT
beql    1f
movw    0(%sp), %r0
brb     9f

1:

bitb    $4, %r0         # CIF_FLAGS_INT
beql    1f
movl    0(%sp), %r0
brb     9f

1:

bitb    $8, %r0         # CIF_FLAGS_DINT
beql    1f
movq    0(%sp), %r0
#brb    9f

1:

9:

ret

/*

* ffi_closure_struct_elfbsd(void);
* invoked with %r0: ffi_closure *closure
*              %r1: struct return address
*/
       .globl  ffi_closure_struct_elfbsd
       .type   ffi_closure_struct_elfbsd, @function
       .align  2

ffi_closure_struct_elfbsd:

.word   0

# Invoke the closure function
pushal  4(%ap)          # calling stack
pushl   %r1             # return value
pushl   %r0             # closure
calls   $3, ffi_closure_elfbsd_inner

ret