/*
 * Copyright 2024 Google LLC
 *
 * 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.
 */

///////////////////////////////////////
// Implements:
//   int setjmp(jmp_buf env);
//   void longjmp(jmp_buf buf, int value);
///////////////////////////////////////
// Notes:
//   This is entirely non-portable. It will need to be rewritten if we stop using ARM.
//   Unfortunately, there is no portable way to define these.
//   This means unit tests can't use our setjmp/longjmp either.

#include <include/setjmp.h>

#if __ARM_ARCH_ISA_THUMB >= 2
// Valid for anything with THUMB2

int __attribute__((__naked__)) setjmp(jmp_buf env) {
  __asm volatile (
  // move SP to a register we can store from and don't need to save
  "mov  %ip, %sp\n"
  // store all the registers
  "stmia  %r0!, {%r4-%r9, %sl, %fp, %ip, %lr}\n"
// using real FPU
#if defined(__VFP_FP__) && !defined(__SOFTFP__)
  // store FP registers
  "vstmia %r0!, {%s16-%s31}\n"
  // store FPSCR
  "vmrs %r1, fpscr\n"
  "str  %r1, [%r0], #4\n"
#endif
  // return 0
  "mov  %r0, #0\n"
  "bx lr\n"
  );
}

void __attribute__((__naked__)) longjmp(jmp_buf buf, int value) {
  __asm volatile (
  // load all the registers
  "ldmia  %r0!, {%r4-%r9, %sl, %fp, %ip, %lr}\n"
  // load SP from a register we could load to and don't need to restore
  "mov  %sp, %ip\n"
// using real FPU
#if defined(__VFP_FP__) && !defined(__SOFTFP__)
  // load FP registers
  "vldmia %r0!, {%s16-%s31}\n"
  // load FPSCR
  "ldr  %r2, [%r0], #4\n"
  "vmsr fpscr, %r2\n"
#endif
  // return value
  "movs %r0, %r1\n"
  // unless it's 0, in which case, return 1.
  "it eq\n"
  "moveq  %r0, #1\n"
  "bx lr\n"
  );
}
#else
// Undefined implementations, don't implement!
// That way we get a link-time error.
#endif
