/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * 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.
 */
/*
 * Invoking JNI native method via SH4 ABI.
 * This inplementation follows the spec found in following URL.
 * http://www.ecos.sourceware.org/docs-1.3.1/ref/gnupro-ref/sh/SH_ch01.html#pgfId-461254

 * This version supports SH4A little endian.
 */
    .text
    .align 4
    .type  dvmPlatformInvoke, #function
    .globl dvmPlatformInvoke

/*
 * @param r4 void* pEnv  (used as scrach after invoking method)
 * @param r5 ClassObject* clazz
 * @param r6 int argInfo
 * @param r7 int argc
 * @param r15[0] const u4 * argv
 * @param r15[1] const char * shorty
 * @param r15[2] void * func
 * @param r15[3] JValue * pReturn
 *
 * @remark r0,r1  Scratch before invoking method.
 *                Return value after invoking method.
 * @remark r2  shorty pointer
 * @remark r3  argv pointer before invoking method.
 *             pReturn after invoking method.
 * @remark r8-11 Don't touch.
 * @remark r12 status of r5-7
 * @remark r13 status of fr4-11
 * @remark r14 Keep stack pointer.
 */
dvmPlatformInvoke:
    ## save preserved regsiters
    mov.l   r14, @-r15
    mov     r15, r14
    add     #4, r14             /* r14 = original r15 = stack pointer */
    mov.l   r13, @-r15
    mov.l   r12, @-r15
    sts.l   pr, @-r15

    # fetch arguments
    mov.l   @r14, r3            /* argv */
    mov.l   @(4,r14), r2        /* shorty for argumnets */
    mov     #1, r0              /* shorty's 1st byte specify ret value type. */
    add     r0, r2

### initialize local variables

    ## r12 ... status of r6, and r7
    ##          bit 1 << 0 : if r6 is available, it contains 1.
    ##          bit 1 << 1 : if r7 is available, it contains 1.
    ##  Note : r4 is always used to pass pEnv.
    ##         r5 is always used for clazz or object
    mov     #3, r12             /* b0000-0111 : r5-7 avialble. */

    ## r13 ... status of fr4-fr11
    ##          bit 1 << 0 : if fr4 is available, it contains 1.
    ##          bit 1 << 1 : if fr5 is available, it contains 1.
    ##      ...
    ##          bit 1 << 7 : if fr11 is available, it contains 1.
    mov     #0xFF, r13          /* b1111-1111 : fr4-11 avialble. */

### put arguments

    ## ... keep pEnv in r4 as is.

    ## check clazz
    mov     #0, r0
    cmp/eq  r0, r5
    bf      arg_loop            /* if r5 has clazz, keep it as is */
    mov.l   @r3+, r5            /* put object arg in r5 */

    ## other args
arg_loop:
one_arg_handled:
    mov.b   @r2+, r0
    cmp/eq  #0, r0              /* if (*shorty == '\0) */
    bf      process_one_arg
    bra     arg_end             /* no argument left */
    nop

process_one_arg:

    ## check arg type

    cmp/eq  #'F', r0
    bt      jfloat_arg

    cmp/eq  #'D', r0
    bt      jdouble_arg

    cmp/eq  #'J', r0
    bt      jlong_arg

    ## other 32bit arg types
    mov     r12, r0
    cmp/eq  #0, r0
    bt      put_32bit_on_stack  /* r6-7 not available */

    tst     #1, r0
    bt      j32_arg_1
    mov.l   @r3+, r6            /* put one arg in r6 */
    mov     #1, r0              /* r6 is not available now. */
    not     r0, r0
    and     r0, r12
    bra     one_arg_handled
    nop
j32_arg_1:
    tst     #2, r0
    bt      j32_arg_fatal_error
    mov.l   @r3+, r7            /* put one arg in r7 */
    mov     #2, r0              /* r7 is not available now. */
    not     r0, r0
    and     r0, r12
    bra     one_arg_handled
    nop

j32_arg_fatal_error:
    bra     j32_arg_fatal_error
    nop

jlong_arg:
    mov     r12, r0
    cmp/eq  #0, r0
    bt      put_64bit_on_stack  /* r6-7 not available */

    and     #3, r0
    cmp/eq  #3, r0
    bf      put_64bit_on_stack  /* consequent two registers not available. */
    mov.l   @r3+, r6            /* put one arg in r6 and r7 */
    mov.l   @r3+, r7
    mov     #3, r0              /* r6 and r7 are not available now. */
    not     r0, r0
    and     r0, r12
    bra     one_arg_handled
    nop

    # utility routines are placed here make short range jumps available.
put_32bit_on_stack:
    mov.l   @r3+, r0
    mov.l   r0, @-r15
    bra     one_arg_handled
    nop

put_64bit_on_stack:
    mov.l   @r3+, r0
    mov.l   r0, @-r15           /* Pay attention that the endianness is */
    mov.l   @r3+, r0            /* once reversed.  It is corrected when the */
    mov.l   r0, @-r15           /* arguments on stack are revesred before */
    bra     one_arg_handled     /* jni call */
    nop

jdouble_arg:
    mov     r13, r0
    cmp/eq  #0, r0
    bt      put_64bit_on_stack  /* fr4-11 not available */

    and     #3, r0
    cmp/eq  #3, r0
    bf      jdouble_arg_1

    fmov.s  @r3+, fr5           /* put one arg to drX */
    fmov.s  @r3+, fr4
    mov     #3, r0              /* fr4-frX not available now. */
    not     r0, r0
    and     r0, r13
    bra     one_arg_handled
    nop

jdouble_arg_1:
    mov     r13, r0
    and     #12, r0
    cmp/eq  #12, r0
    bf      jdouble_arg_2

    fmov.s  @r3+, fr7           /* put one arg to drX */
    fmov.s  @r3+, fr6
    mov     #15, r0             /* fr4-frX not available now. */
    not     r0, r0
    and     r0, r13
    bra     one_arg_handled
    nop

jdouble_arg_2:
    mov     r13, r0
    and     #48, r0
    cmp/eq  #48, r0
    bf      jdouble_arg_3
    fmov.s  @r3+, fr9           /* put one arg to drX */
    fmov.s  @r3+, fr8
    mov     #63, r0             /* fr4-frX not available now. */
    not     r0, r0
    and     r0, r13
    bra     one_arg_handled
    nop

jdouble_arg_3:
    mov     r13, r0
    and     #192, r0
    cmp/eq  #192, r0
    bf      put_64bit_on_stack
    fmov.s  @r3+, fr11          /* put one arg to drX */
    fmov.s  @r3+, fr10
    mov     #0, r13             /* fr4-fr11 all not available now. */
    bra     one_arg_handled
    nop

jfloat_arg:
    mov     r13, r0
    cmp/eq  #0, r0
    bt      put_32bit_on_stack  /* fr4-11 not available */

    tst     #2, r0
    bt      jfloat_arg_1
    fmov.s  @r3+, fr5           /* put one arg to frX */
    mov     #2, r0              /* frX not available now. */
    not     r0, r0
    and     r0, r13
    bra     one_arg_handled
    nop

jfloat_arg_1:
    tst     #1, r0
    bt      jfloat_arg_2
    fmov.s  @r3+, fr4           /* put one arg to frX */
    mov     #1, r0              /* frX not available now. */
    not     r0, r0
    and     r0, r13
    bra     one_arg_handled
    nop

jfloat_arg_2:
    tst     #8, r0
    bt      jfloat_arg_3
    fmov.s  @r3+, fr7           /* put one arg to frX */
    mov     #8, r0              /* frX not available now. */
    not     r0, r0
    and     r0, r13
    bra     one_arg_handled
    nop

jfloat_arg_3:
    tst     #4, r0
    bt      jfloat_arg_4
    fmov.s  @r3+, fr6           /* put one arg to frX */
    mov     #4, r0              /* frX not available now. */
    not     r0, r0
    and     r0, r13
    bra     one_arg_handled
    nop

jfloat_arg_4:
    tst     #32, r0
    bt      jfloat_arg_5
    fmov.s  @r3+, fr9           /* put one arg to frX */
    mov     #32, r0             /* frX not available now. */
    not     r0, r0
    and     r0, r13
    bra     one_arg_handled
    nop

jfloat_arg_5:
    tst     #16, r0
    bt      jfloat_arg_6
    fmov.s  @r3+, fr8           /* put one arg to frX */
    mov     #16, r0             /* frX not available now. */
    not     r0, r0
    and     r0, r13
    bra     one_arg_handled
    nop

jfloat_arg_6:
    tst     #128, r0
    bt      jfloat_arg_7
    fmov.s  @r3+, fr11          /* put one arg to frX */
    mov     #127, r0            /* frX not available now. */
    not     r0, r0
    and     r0, r13
    bra     one_arg_handled
    nop

jfloat_arg_7:
    tst     #64, r0
    bt      jfloat_fatal_error
    fmov.s  @r3+, fr10          /* put one arg to frX */
    mov     #64, r0             /* frX not available now. */
    not     r0, r0
    and     r0, r13
    bra     one_arg_handled
    nop

jfloat_fatal_error:
    bra     jfloat_fatal_error:
    nop

arg_end:


### reverse the variables on stack
    mov     r14, r12            /* points to first arg on stack */
    add     #-20, r12
    mov     r15, r13            /* points to last arg on stack */
arg_rev_loop:
    cmp/hs  r12, r13            /* When r13 >= r12 (unsigned), 1->T */
    bt      arg_rev_end
    mov.l   @r12, r0
    mov.l   @r13, r1
    mov.l   r0, @r13
    mov.l   r1, @r12
    add     #-4, r12
    add     #4, r13
    bra     arg_rev_loop
    nop

arg_rev_end:

### invoke the JNI function.

    mov.l   @(8,r14), r0
    jsr     @r0
    nop

### pass the return value

    /*
     * r0 and r1 keep return value.
     */

    ## fetch data
    mov.l   @(4,r14), r2        /* reload shorty */
    mov.b   @r2, r2             /* first byte specifyes return value type. */
    mov.l   @(12,r14), r3       /* pReturn */

    ## check return value types

    mov     #'V', r4
    cmp/eq  r4, r2
    bt      end

    mov     #'F', r4
    cmp/eq  r4, r2
    bt      jfloat_ret

    mov     #'D', r4
    cmp/eq  r4, r2
    bt      jdouble_ret

    mov     #'J', r4
    cmp/eq  r4, r2
    bt      jlong_ret

    ## fall-through for other 32 bit java types.

    ## load return values
j32_ret:
    bra     end
    mov.l   r0, @r3             /* delay slot */

jfloat_ret:
    bra     end
    fmov.s  fr0, @r3            /* delay slot */

jdouble_ret:
    fmov.s  fr1, @r3
    mov     #4, r0
    bra     end
    fmov.s  fr0, @(r0,r3)       /* delay slot */

jlong_ret:
    mov.l   r0, @r3
    bra     end
    mov.l   r1, @(4,r3)         /* delay slot */

end:
    ## restore preserved registers
    mov     r14, r15
    add     #-16, r15
    lds.l   @r15+, pr
    mov.l   @r15+, r12
    mov.l   @r15+, r13
    mov.l   @r15+, r14

    rts                         /* dvmPlatformInvoke returns void. */
    nop