/* Copyright 2014-2015 Samsung Electronics Co., 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.
 */

#ifndef ASM_ARM_H
#define ASM_ARM_H

/*
 * mov syscall_no (%r0) -> %r7
 * svc #0
 */
#define SYSCALL_0 \
  push {r4-r12, lr}; \
  \
  mov r7, r0; \
  \
  svc #0; \
  \
  pop {r4-r12, pc};

/*
 * mov syscall_no (%r0) -> %r7
 * mov arg1 (%r1) -> %r0
 * svc #0
 */
#define SYSCALL_1 \
  push {r4-r12, lr}; \
  \
  mov r7, r0; \
  mov r0, r1; \
  \
  svc #0; \
  \
  pop {r4-r12, pc};

/*
 * mov syscall_no (%r0) -> %r7
 * mov arg1 (%r1) -> %r0
 * mov arg2 (%r2) -> %r1
 * svc #0
 */
#define SYSCALL_2 \
  push {r4-r12, lr}; \
  \
  mov r7, r0; \
  mov r0, r1; \
  mov r1, r2; \
  \
  svc #0; \
  \
  pop {r4-r12, pc};

/*
 * mov syscall_no (%r0) -> %r7
 * mov arg1 (%r1) -> %r0
 * mov arg2 (%r2) -> %r1
 * mov arg3 (%r3) -> %r2
 * svc #0
 */
#define SYSCALL_3 \
  push {r4-r12, lr}; \
  \
  mov r7, r0; \
  mov r0, r1; \
  mov r1, r2; \
  mov r2, r3; \
  \
  svc #0; \
  \
  pop {r4-r12, pc};

/*
 * ldr argc ([sp + 0x0]) -> r0
 * add argv (sp + 0x4) -> r1
 *
 * bl main
 *
 * bl exit
 *
 * infinite loop
 */
#define _START            \
   ldr r0, [sp, #0];      \
   add r1, sp, #4;        \
   bl main;               \
                          \
   bl exit;               \
   1:                     \
   b 1b

/**
 * If hard-float mode:
 *   store s16-s31 vfp registers to buffer, pointed with r0 register,
 *   and increase the register on size of stored data.
 */
#ifdef __TARGET_HOST_ARMv7_HARD_FLOAT
# define _STORE_VFP_S16_S31_IF_HARD_FLOAT \
   vstm r0!, {s16 - s31};
# define _LOAD_VFP_S16_S31_IF_HARD_FLOAT \
   vldm r0!, {s16 - s31};
#else /* !__TARGET_HOST_ARMv7_HARD_FLOAT */
# define _STORE_VFP_S16_S31_IF_HARD_FLOAT
# define _LOAD_VFP_S16_S31_IF_HARD_FLOAT
#endif /* !__TARGET_HOST_ARMv7_HARD_FLOAT */

/*
 * setjmp
 *
 * According to procedure call standard for the ARM architecture, the following
 * registers are callee-saved, and so need to be stored in context:
 *   - r4 - r11
 *   - sp
 *   - s16 - s31
 *
 * Also, we should store:
 *   - lr
 *
 * stmia {r4-r11, sp, lr} -> jmp_buf_0 (r0)!
 *
 * If hard-float build
 *   vstm  {s16-s31} -> jmp_buf_32 (r0)!
 *
 * mov r0, #0
 *
 * bx lr
 */
#define _SETJMP \
  stmia r0!, {r4 - r11, sp, lr};    \
                                    \
  _STORE_VFP_S16_S31_IF_HARD_FLOAT  \
                                    \
  mov r0, #0;                       \
                                    \
  bx lr;

/*
 * longjmp
 *
 * See also:
 *          _SETJMP
 *
 * ldmia jmp_buf_0 (r0)! -> {r4-r11, sp, lr}
 *
 * If hard-float build
 *   vldm  jmp_buf_32 (r0)! -> {s16-s31}
 *
 * mov r1 -> r0
 * cmp r0, #0
 * bne 1f
 * mov #1 -> r0
 * 1:
 *
 * bx lr
 */
#define _LONGJMP \
  ldmia r0!, {r4 - r11, sp, lr};    \
                                    \
  _LOAD_VFP_S16_S31_IF_HARD_FLOAT   \
                                    \
  mov r0, r1;                       \
  cmp r0, #0;                       \
  bne 1f;                           \
  mov r0, #1;                       \
  1:                                \
                                    \
  bx lr;

#endif /* !ASM_ARM_H */
