/*
 * This file is part of the µOS++ project (https://micro-os-plus.github.io/).
 * Copyright (c) 2023-2026 Liviu Ionescu. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose is hereby granted, under the terms of the MIT license.
 *
 * If a copy of the license was not distributed with this file, it can be
 * obtained from https://opensource.org/licenses/mit.
 */

// ----------------------------------------------------------------------------

#include "micro-os-plus/architecture.h"
#include "micro-os-plus/semihosting.h"

// ----------------------------------------------------------------------------

#if defined(MICRO_OS_PLUS_SEMIHOSTING_ENABLED)

// ----------------------------------------------------------------------------

#ifdef __thumb__
#define AngelSWI 0xAB
#define AngelSWIInsn "bkpt"
#define AngelSWIAsm bkpt
#else
#error "Unsupported architecture."
#endif

#if defined(MICRO_OS_PLUS_SEMIHOSTING_DEBUG_FAULTS_ENABLED)
// Testing the local semihosting handler cannot use another BKPT, since this
// configuration cannot trigger HardFault exceptions while the debugger is
// connected, so we use an illegal op code, that will trigger an
// UsageFault exception.
#define AngelSWITestFault "setend be"
#define AngelSWITestFaultOpCode (0xB658)
#endif // defined(MICRO_OS_PLUS_SEMIHOSTING_DEBUG_FAULTS_ENABLED)

micro_os_plus_semihosting_response_t
micro_os_plus_semihosting_call_host (
    int reason, micro_os_plus_semihosting_param_block_t* arg)
{
  micro_os_plus_semihosting_response_t value;
  __asm__ volatile (

      " mov r0, %[rsn]  \n"
      " mov r1, %[arg]  \n"
#if defined(MICRO_OS_PLUS_SEMIHOSTING_DEBUG_FAULTS_ENABLED)
      " " AngelSWITestFault " \n"
#else
      " " AngelSWIInsn " %[swi] \n"
#endif // defined(MICRO_OS_PLUS_SEMIHOSTING_DEBUG_FAULTS_ENABLED)
      " mov %[val], r0"

      : [val] "=r"(value) /* Outputs */
      : [rsn] "r"(reason), [arg] "r"(arg), [swi] "i"(AngelSWI) /* Inputs */
      : "r0", "r1", "r2", "r3", "ip", "lr", "memory", "cc"
      // Clobbers r0 and r1, and lr if in supervisor mode
  );

  // Accordingly to page 13-77 of ARM DUI 0040D other registers
  // can also be clobbered. Some memory positions may also be
  // changed by a system call, so they should not be kept in
  // registers. Note: we are assuming the manual is right and
  // Angel is respecting the APCS.
  return value;
}

// ----------------------------------------------------------------------------

#endif // defined(MICRO_OS_PLUS_SEMIHOSTING_ENABLED)

// ----------------------------------------------------------------------------
